@code-pushup/cli 0.35.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js
CHANGED
|
@@ -6,9 +6,6 @@ import { hideBin } from "yargs/helpers";
|
|
|
6
6
|
// packages/cli/src/lib/autorun/autorun-command.ts
|
|
7
7
|
import chalk7 from "chalk";
|
|
8
8
|
|
|
9
|
-
// packages/models/src/lib/audit.ts
|
|
10
|
-
import { z as z2 } from "zod";
|
|
11
|
-
|
|
12
9
|
// packages/models/src/lib/implementation/schemas.ts
|
|
13
10
|
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
14
11
|
import { z } from "zod";
|
|
@@ -74,6 +71,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
|
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
// packages/models/src/lib/implementation/schemas.ts
|
|
74
|
+
var primitiveValueSchema = z.union([z.string(), z.number()]);
|
|
77
75
|
function executionMetaSchema(options2 = {
|
|
78
76
|
descriptionDate: "Execution start date and time",
|
|
79
77
|
descriptionDuration: "Execution duration in ms"
|
|
@@ -165,6 +163,7 @@ function hasNonZeroWeightedRef(refs) {
|
|
|
165
163
|
}
|
|
166
164
|
|
|
167
165
|
// packages/models/src/lib/audit.ts
|
|
166
|
+
import { z as z2 } from "zod";
|
|
168
167
|
var auditSchema = z2.object({
|
|
169
168
|
slug: slugSchema.describe("ID (unique within plugin)")
|
|
170
169
|
}).merge(
|
|
@@ -194,7 +193,7 @@ function getDuplicateSlugsInAudits(audits) {
|
|
|
194
193
|
}
|
|
195
194
|
|
|
196
195
|
// packages/models/src/lib/audit-output.ts
|
|
197
|
-
import { z as
|
|
196
|
+
import { z as z5 } from "zod";
|
|
198
197
|
|
|
199
198
|
// packages/models/src/lib/issue.ts
|
|
200
199
|
import { z as z3 } from "zod";
|
|
@@ -225,16 +224,61 @@ var issueSchema = z3.object(
|
|
|
225
224
|
{ description: "Issue information" }
|
|
226
225
|
);
|
|
227
226
|
|
|
227
|
+
// packages/models/src/lib/table.ts
|
|
228
|
+
import { z as z4 } from "zod";
|
|
229
|
+
var tableAlignmentSchema = z4.enum(["left", "center", "right"], {
|
|
230
|
+
description: "Cell alignment"
|
|
231
|
+
});
|
|
232
|
+
var tableColumnObjectSchema = z4.object({
|
|
233
|
+
key: z4.string(),
|
|
234
|
+
label: z4.string().optional(),
|
|
235
|
+
align: tableAlignmentSchema.optional()
|
|
236
|
+
});
|
|
237
|
+
var tableRowObjectSchema = z4.record(primitiveValueSchema, {
|
|
238
|
+
description: "Object row"
|
|
239
|
+
});
|
|
240
|
+
var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
|
|
241
|
+
description: "Primitive row"
|
|
242
|
+
});
|
|
243
|
+
var tableSharedSchema = z4.object({
|
|
244
|
+
title: z4.string().optional().describe("Display title for table")
|
|
245
|
+
});
|
|
246
|
+
var tablePrimitiveSchema = tableSharedSchema.merge(
|
|
247
|
+
z4.object(
|
|
248
|
+
{
|
|
249
|
+
columns: z4.array(tableAlignmentSchema).optional(),
|
|
250
|
+
rows: z4.array(tableRowPrimitiveSchema)
|
|
251
|
+
},
|
|
252
|
+
{ description: "Table with primitive rows and optional alignment columns" }
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
var tableObjectSchema = tableSharedSchema.merge(
|
|
256
|
+
z4.object(
|
|
257
|
+
{
|
|
258
|
+
columns: z4.union([
|
|
259
|
+
z4.array(tableAlignmentSchema),
|
|
260
|
+
z4.array(tableColumnObjectSchema)
|
|
261
|
+
]).optional(),
|
|
262
|
+
rows: z4.array(tableRowObjectSchema)
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
description: "Table with object rows and optional alignment or object columns"
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
|
|
270
|
+
|
|
228
271
|
// packages/models/src/lib/audit-output.ts
|
|
229
272
|
var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
|
|
230
|
-
var auditDisplayValueSchema =
|
|
231
|
-
var auditDetailsSchema =
|
|
273
|
+
var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
274
|
+
var auditDetailsSchema = z5.object(
|
|
232
275
|
{
|
|
233
|
-
issues:
|
|
276
|
+
issues: z5.array(issueSchema, { description: "List of findings" }).optional(),
|
|
277
|
+
table: tableSchema("Table of related findings").optional()
|
|
234
278
|
},
|
|
235
279
|
{ description: "Detailed information" }
|
|
236
280
|
);
|
|
237
|
-
var auditOutputSchema =
|
|
281
|
+
var auditOutputSchema = z5.object(
|
|
238
282
|
{
|
|
239
283
|
slug: slugSchema.describe("Reference to audit"),
|
|
240
284
|
displayValue: auditDisplayValueSchema,
|
|
@@ -244,7 +288,7 @@ var auditOutputSchema = z4.object(
|
|
|
244
288
|
},
|
|
245
289
|
{ description: "Audit information" }
|
|
246
290
|
);
|
|
247
|
-
var auditOutputsSchema =
|
|
291
|
+
var auditOutputsSchema = z5.array(auditOutputSchema, {
|
|
248
292
|
description: "List of JSON formatted audit output emitted by the runner process of a plugin"
|
|
249
293
|
}).refine(
|
|
250
294
|
(audits) => !getDuplicateSlugsInAudits2(audits),
|
|
@@ -261,13 +305,13 @@ function getDuplicateSlugsInAudits2(audits) {
|
|
|
261
305
|
}
|
|
262
306
|
|
|
263
307
|
// packages/models/src/lib/category-config.ts
|
|
264
|
-
import { z as
|
|
308
|
+
import { z as z6 } from "zod";
|
|
265
309
|
var categoryRefSchema = weightedRefSchema(
|
|
266
310
|
"Weighted references to audits and/or groups for the category",
|
|
267
311
|
"Slug of an audit or group (depending on `type`)"
|
|
268
312
|
).merge(
|
|
269
|
-
|
|
270
|
-
type:
|
|
313
|
+
z6.object({
|
|
314
|
+
type: z6.enum(["audit", "group"], {
|
|
271
315
|
description: "Discriminant for reference kind, affects where `slug` is looked up"
|
|
272
316
|
}),
|
|
273
317
|
plugin: slugSchema.describe(
|
|
@@ -288,8 +332,8 @@ var categoryConfigSchema = scorableSchema(
|
|
|
288
332
|
description: "Meta info for category"
|
|
289
333
|
})
|
|
290
334
|
).merge(
|
|
291
|
-
|
|
292
|
-
isBinary:
|
|
335
|
+
z6.object({
|
|
336
|
+
isBinary: z6.boolean({
|
|
293
337
|
description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
|
|
294
338
|
}).optional()
|
|
295
339
|
})
|
|
@@ -305,7 +349,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
|
|
|
305
349
|
metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
|
|
306
350
|
);
|
|
307
351
|
}
|
|
308
|
-
var categoriesSchema =
|
|
352
|
+
var categoriesSchema = z6.array(categoryConfigSchema, {
|
|
309
353
|
description: "Categorization of individual audits"
|
|
310
354
|
}).refine(
|
|
311
355
|
(categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
|
|
@@ -324,18 +368,18 @@ function getDuplicateSlugCategories(categories) {
|
|
|
324
368
|
}
|
|
325
369
|
|
|
326
370
|
// packages/models/src/lib/commit.ts
|
|
327
|
-
import { z as
|
|
328
|
-
var commitSchema =
|
|
371
|
+
import { z as z7 } from "zod";
|
|
372
|
+
var commitSchema = z7.object(
|
|
329
373
|
{
|
|
330
|
-
hash:
|
|
374
|
+
hash: z7.string({ description: "Commit SHA (full)" }).regex(
|
|
331
375
|
/^[\da-f]{40}$/,
|
|
332
376
|
"Commit SHA should be a 40-character hexadecimal string"
|
|
333
377
|
),
|
|
334
|
-
message:
|
|
335
|
-
date:
|
|
378
|
+
message: z7.string({ description: "Commit message" }),
|
|
379
|
+
date: z7.coerce.date({
|
|
336
380
|
description: "Date and time when commit was authored"
|
|
337
381
|
}),
|
|
338
|
-
author:
|
|
382
|
+
author: z7.string({
|
|
339
383
|
description: "Commit author name"
|
|
340
384
|
}).trim()
|
|
341
385
|
},
|
|
@@ -343,22 +387,22 @@ var commitSchema = z6.object(
|
|
|
343
387
|
);
|
|
344
388
|
|
|
345
389
|
// packages/models/src/lib/core-config.ts
|
|
346
|
-
import { z as
|
|
390
|
+
import { z as z13 } from "zod";
|
|
347
391
|
|
|
348
392
|
// packages/models/src/lib/persist-config.ts
|
|
349
|
-
import { z as
|
|
350
|
-
var formatSchema =
|
|
351
|
-
var persistConfigSchema =
|
|
393
|
+
import { z as z8 } from "zod";
|
|
394
|
+
var formatSchema = z8.enum(["json", "md"]);
|
|
395
|
+
var persistConfigSchema = z8.object({
|
|
352
396
|
outputDir: filePathSchema.describe("Artifacts folder").optional(),
|
|
353
397
|
filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
|
|
354
|
-
format:
|
|
398
|
+
format: z8.array(formatSchema).optional()
|
|
355
399
|
});
|
|
356
400
|
|
|
357
401
|
// packages/models/src/lib/plugin-config.ts
|
|
358
|
-
import { z as
|
|
402
|
+
import { z as z11 } from "zod";
|
|
359
403
|
|
|
360
404
|
// packages/models/src/lib/group.ts
|
|
361
|
-
import { z as
|
|
405
|
+
import { z as z9 } from "zod";
|
|
362
406
|
var groupRefSchema = weightedRefSchema(
|
|
363
407
|
"Weighted reference to a group",
|
|
364
408
|
"Reference slug to a group within this plugin (e.g. 'max-lines')"
|
|
@@ -375,7 +419,7 @@ var groupSchema = scorableSchema(
|
|
|
375
419
|
getDuplicateRefsInGroups,
|
|
376
420
|
duplicateRefsInGroupsErrorMsg
|
|
377
421
|
).merge(groupMetaSchema);
|
|
378
|
-
var groupsSchema =
|
|
422
|
+
var groupsSchema = z9.array(groupSchema, {
|
|
379
423
|
description: "List of groups"
|
|
380
424
|
}).optional().refine(
|
|
381
425
|
(groups2) => !getDuplicateSlugsInGroups(groups2),
|
|
@@ -403,14 +447,14 @@ function getDuplicateSlugsInGroups(groups2) {
|
|
|
403
447
|
}
|
|
404
448
|
|
|
405
449
|
// packages/models/src/lib/runner-config.ts
|
|
406
|
-
import { z as
|
|
407
|
-
var outputTransformSchema =
|
|
408
|
-
var runnerConfigSchema =
|
|
450
|
+
import { z as z10 } from "zod";
|
|
451
|
+
var outputTransformSchema = z10.function().args(z10.unknown()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
|
|
452
|
+
var runnerConfigSchema = z10.object(
|
|
409
453
|
{
|
|
410
|
-
command:
|
|
454
|
+
command: z10.string({
|
|
411
455
|
description: "Shell command to execute"
|
|
412
456
|
}),
|
|
413
|
-
args:
|
|
457
|
+
args: z10.array(z10.string({ description: "Command arguments" })).optional(),
|
|
414
458
|
outputFile: filePathSchema.describe("Output path"),
|
|
415
459
|
outputTransform: outputTransformSchema.optional()
|
|
416
460
|
},
|
|
@@ -418,8 +462,8 @@ var runnerConfigSchema = z9.object(
|
|
|
418
462
|
description: "How to execute runner"
|
|
419
463
|
}
|
|
420
464
|
);
|
|
421
|
-
var onProgressSchema =
|
|
422
|
-
var runnerFunctionSchema =
|
|
465
|
+
var onProgressSchema = z10.function().args(z10.unknown()).returns(z10.void());
|
|
466
|
+
var runnerFunctionSchema = z10.function().args(onProgressSchema.optional()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
|
|
423
467
|
|
|
424
468
|
// packages/models/src/lib/plugin-config.ts
|
|
425
469
|
var pluginMetaSchema = packageVersionSchema().merge(
|
|
@@ -430,13 +474,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
|
|
|
430
474
|
description: "Plugin metadata"
|
|
431
475
|
})
|
|
432
476
|
).merge(
|
|
433
|
-
|
|
477
|
+
z11.object({
|
|
434
478
|
slug: slugSchema.describe("Unique plugin slug within core config"),
|
|
435
479
|
icon: materialIconSchema
|
|
436
480
|
})
|
|
437
481
|
);
|
|
438
|
-
var pluginDataSchema =
|
|
439
|
-
runner:
|
|
482
|
+
var pluginDataSchema = z11.object({
|
|
483
|
+
runner: z11.union([runnerConfigSchema, runnerFunctionSchema]),
|
|
440
484
|
audits: pluginAuditsSchema,
|
|
441
485
|
groups: groupsSchema
|
|
442
486
|
});
|
|
@@ -462,22 +506,22 @@ function getMissingRefsFromGroups(pluginCfg) {
|
|
|
462
506
|
}
|
|
463
507
|
|
|
464
508
|
// packages/models/src/lib/upload-config.ts
|
|
465
|
-
import { z as
|
|
466
|
-
var uploadConfigSchema =
|
|
509
|
+
import { z as z12 } from "zod";
|
|
510
|
+
var uploadConfigSchema = z12.object({
|
|
467
511
|
server: urlSchema.describe("URL of deployed portal API"),
|
|
468
|
-
apiKey:
|
|
512
|
+
apiKey: z12.string({
|
|
469
513
|
description: "API key with write access to portal (use `process.env` for security)"
|
|
470
514
|
}),
|
|
471
515
|
organization: slugSchema.describe(
|
|
472
516
|
"Organization slug from Code PushUp portal"
|
|
473
517
|
),
|
|
474
518
|
project: slugSchema.describe("Project slug from Code PushUp portal"),
|
|
475
|
-
timeout:
|
|
519
|
+
timeout: z12.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
|
|
476
520
|
});
|
|
477
521
|
|
|
478
522
|
// packages/models/src/lib/core-config.ts
|
|
479
|
-
var unrefinedCoreConfigSchema =
|
|
480
|
-
plugins:
|
|
523
|
+
var unrefinedCoreConfigSchema = z13.object({
|
|
524
|
+
plugins: z13.array(pluginConfigSchema, {
|
|
481
525
|
description: "List of plugins to be used (official, community-provided, or custom)"
|
|
482
526
|
}).min(1),
|
|
483
527
|
/** portal configuration for persisting results */
|
|
@@ -509,7 +553,7 @@ var DEFAULT_PERSIST_FILENAME = "report";
|
|
|
509
553
|
var DEFAULT_PERSIST_FORMAT = ["json", "md"];
|
|
510
554
|
|
|
511
555
|
// packages/models/src/lib/report.ts
|
|
512
|
-
import { z as
|
|
556
|
+
import { z as z14 } from "zod";
|
|
513
557
|
var auditReportSchema = auditSchema.merge(auditOutputSchema);
|
|
514
558
|
var pluginReportSchema = pluginMetaSchema.merge(
|
|
515
559
|
executionMetaSchema({
|
|
@@ -517,9 +561,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
|
|
|
517
561
|
descriptionDuration: "Duration of the plugin run in ms"
|
|
518
562
|
})
|
|
519
563
|
).merge(
|
|
520
|
-
|
|
521
|
-
audits:
|
|
522
|
-
groups:
|
|
564
|
+
z14.object({
|
|
565
|
+
audits: z14.array(auditReportSchema).min(1),
|
|
566
|
+
groups: z14.array(groupSchema).optional()
|
|
523
567
|
})
|
|
524
568
|
).refine(
|
|
525
569
|
(pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
|
|
@@ -553,10 +597,10 @@ var reportSchema = packageVersionSchema({
|
|
|
553
597
|
descriptionDuration: "Duration of the collect run in ms"
|
|
554
598
|
})
|
|
555
599
|
).merge(
|
|
556
|
-
|
|
600
|
+
z14.object(
|
|
557
601
|
{
|
|
558
|
-
categories:
|
|
559
|
-
plugins:
|
|
602
|
+
categories: z14.array(categoryConfigSchema),
|
|
603
|
+
plugins: z14.array(pluginReportSchema).min(1),
|
|
560
604
|
commit: commitSchema.describe("Git commit for which report was collected").nullable()
|
|
561
605
|
},
|
|
562
606
|
{ description: "Collect output data" }
|
|
@@ -572,36 +616,40 @@ var reportSchema = packageVersionSchema({
|
|
|
572
616
|
);
|
|
573
617
|
|
|
574
618
|
// packages/models/src/lib/reports-diff.ts
|
|
575
|
-
import { z as
|
|
619
|
+
import { z as z15 } from "zod";
|
|
576
620
|
function makeComparisonSchema(schema) {
|
|
577
621
|
const sharedDescription = schema.description || "Result";
|
|
578
|
-
return
|
|
622
|
+
return z15.object({
|
|
579
623
|
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
580
624
|
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
581
625
|
});
|
|
582
626
|
}
|
|
583
627
|
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
584
|
-
return
|
|
628
|
+
return z15.object(
|
|
585
629
|
{
|
|
586
|
-
changed:
|
|
587
|
-
unchanged:
|
|
588
|
-
added:
|
|
589
|
-
removed:
|
|
630
|
+
changed: z15.array(diffSchema),
|
|
631
|
+
unchanged: z15.array(resultSchema),
|
|
632
|
+
added: z15.array(resultSchema),
|
|
633
|
+
removed: z15.array(resultSchema)
|
|
590
634
|
},
|
|
591
635
|
{ description }
|
|
592
636
|
);
|
|
593
637
|
}
|
|
594
|
-
var scorableMetaSchema =
|
|
638
|
+
var scorableMetaSchema = z15.object({
|
|
639
|
+
slug: slugSchema,
|
|
640
|
+
title: titleSchema,
|
|
641
|
+
docsUrl: docsUrlSchema
|
|
642
|
+
});
|
|
595
643
|
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
596
|
-
|
|
597
|
-
plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
|
|
644
|
+
z15.object({
|
|
645
|
+
plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
|
|
598
646
|
})
|
|
599
647
|
);
|
|
600
648
|
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
601
|
-
|
|
649
|
+
z15.object({
|
|
602
650
|
scores: makeComparisonSchema(scoreSchema).merge(
|
|
603
|
-
|
|
604
|
-
diff:
|
|
651
|
+
z15.object({
|
|
652
|
+
diff: z15.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
605
653
|
})
|
|
606
654
|
).describe("Score comparison")
|
|
607
655
|
})
|
|
@@ -612,10 +660,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
|
612
660
|
var categoryDiffSchema = scorableDiffSchema;
|
|
613
661
|
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
614
662
|
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
615
|
-
|
|
663
|
+
z15.object({
|
|
616
664
|
values: makeComparisonSchema(auditValueSchema).merge(
|
|
617
|
-
|
|
618
|
-
diff:
|
|
665
|
+
z15.object({
|
|
666
|
+
diff: z15.number().int().describe("Value change (`values.after - values.before`)")
|
|
619
667
|
})
|
|
620
668
|
).describe("Audit `value` comparison"),
|
|
621
669
|
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
@@ -624,15 +672,15 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
|
624
672
|
})
|
|
625
673
|
);
|
|
626
674
|
var categoryResultSchema = scorableMetaSchema.merge(
|
|
627
|
-
|
|
675
|
+
z15.object({ score: scoreSchema })
|
|
628
676
|
);
|
|
629
677
|
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
630
|
-
|
|
678
|
+
z15.object({ score: scoreSchema })
|
|
631
679
|
);
|
|
632
680
|
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
633
681
|
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
634
682
|
);
|
|
635
|
-
var reportsDiffSchema =
|
|
683
|
+
var reportsDiffSchema = z15.object({
|
|
636
684
|
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
637
685
|
categories: makeArraysComparisonSchema(
|
|
638
686
|
categoryDiffSchema,
|
|
@@ -661,6 +709,291 @@ var reportsDiffSchema = z14.object({
|
|
|
661
709
|
})
|
|
662
710
|
);
|
|
663
711
|
|
|
712
|
+
// packages/utils/src/lib/text-formats/constants.ts
|
|
713
|
+
var NEW_LINE = "\n";
|
|
714
|
+
var TAB = " ";
|
|
715
|
+
var SPACE = " ";
|
|
716
|
+
|
|
717
|
+
// packages/utils/src/lib/text-formats/html/details.ts
|
|
718
|
+
function details(title, content, cfg = { open: false }) {
|
|
719
|
+
return `<details${cfg.open ? " open" : ""}>${NEW_LINE}<summary>${title}</summary>${NEW_LINE}${// ⚠️ The blank line is needed to ensure Markdown in content is rendered correctly.
|
|
720
|
+
NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
|
|
721
|
+
// ⚠️ The blank line ensure Markdown in content is rendered correctly.
|
|
722
|
+
NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
|
|
723
|
+
NEW_LINE}`;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// packages/utils/src/lib/text-formats/html/font-style.ts
|
|
727
|
+
var boldElement = "b";
|
|
728
|
+
function bold(text) {
|
|
729
|
+
return `<${boldElement}>${text}</${boldElement}>`;
|
|
730
|
+
}
|
|
731
|
+
var italicElement = "i";
|
|
732
|
+
function italic(text) {
|
|
733
|
+
return `<${italicElement}>${text}</${italicElement}>`;
|
|
734
|
+
}
|
|
735
|
+
var codeElement = "code";
|
|
736
|
+
function code(text) {
|
|
737
|
+
return `<${codeElement}>${text}</${codeElement}>`;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// packages/utils/src/lib/text-formats/html/link.ts
|
|
741
|
+
function link(href, text) {
|
|
742
|
+
return `<a href="${href}">${text || href}"</a>`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// packages/utils/src/lib/transform.ts
|
|
746
|
+
function toArray(val) {
|
|
747
|
+
return Array.isArray(val) ? val : [val];
|
|
748
|
+
}
|
|
749
|
+
function objectToEntries(obj) {
|
|
750
|
+
return Object.entries(obj);
|
|
751
|
+
}
|
|
752
|
+
function deepClone(obj) {
|
|
753
|
+
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
754
|
+
}
|
|
755
|
+
function toUnixPath(path) {
|
|
756
|
+
return path.replace(/\\/g, "/");
|
|
757
|
+
}
|
|
758
|
+
function capitalize(text) {
|
|
759
|
+
return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
|
|
760
|
+
1
|
|
761
|
+
)}`;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// packages/utils/src/lib/table.ts
|
|
765
|
+
function rowToStringArray({ rows, columns = [] }) {
|
|
766
|
+
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
767
|
+
throw new TypeError(
|
|
768
|
+
"Column can`t be object when rows are primitive values"
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
return rows.map((row) => {
|
|
772
|
+
if (Array.isArray(row)) {
|
|
773
|
+
return row.map(String);
|
|
774
|
+
}
|
|
775
|
+
const objectRow = row;
|
|
776
|
+
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
777
|
+
return Object.values(objectRow).map(String);
|
|
778
|
+
}
|
|
779
|
+
return columns.map(
|
|
780
|
+
({ key }) => String(objectRow[key])
|
|
781
|
+
);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
function columnsToStringArray({ rows, columns = [] }) {
|
|
785
|
+
const firstRow = rows.at(0);
|
|
786
|
+
const primitiveRows = Array.isArray(firstRow);
|
|
787
|
+
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
788
|
+
throw new Error("invalid union type. Caught by model parsing.");
|
|
789
|
+
}
|
|
790
|
+
if (columns.length === 0) {
|
|
791
|
+
if (Array.isArray(firstRow)) {
|
|
792
|
+
return firstRow.map((_, idx) => String(idx));
|
|
793
|
+
}
|
|
794
|
+
return Object.keys(firstRow);
|
|
795
|
+
}
|
|
796
|
+
if (typeof columns.at(0) === "string") {
|
|
797
|
+
return columns.map(String);
|
|
798
|
+
}
|
|
799
|
+
const cols = columns;
|
|
800
|
+
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
801
|
+
}
|
|
802
|
+
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
803
|
+
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
804
|
+
if (typeof column === "string") {
|
|
805
|
+
return column;
|
|
806
|
+
} else if (typeof column === "object") {
|
|
807
|
+
return column.align ?? "center";
|
|
808
|
+
} else {
|
|
809
|
+
return "center";
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
813
|
+
const column = columns.at(targetIdx);
|
|
814
|
+
if (column == null) {
|
|
815
|
+
return "center";
|
|
816
|
+
} else if (typeof column === "string") {
|
|
817
|
+
return column;
|
|
818
|
+
} else if (typeof column === "object") {
|
|
819
|
+
return column.align ?? "center";
|
|
820
|
+
} else {
|
|
821
|
+
return "center";
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function getColumnAlignments({
|
|
825
|
+
rows,
|
|
826
|
+
columns = []
|
|
827
|
+
}) {
|
|
828
|
+
if (rows.at(0) == null) {
|
|
829
|
+
throw new Error("first row can`t be undefined.");
|
|
830
|
+
}
|
|
831
|
+
if (Array.isArray(rows.at(0))) {
|
|
832
|
+
const firstPrimitiveRow = rows.at(0);
|
|
833
|
+
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
834
|
+
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
const firstObject = rows.at(0);
|
|
838
|
+
return Object.keys(firstObject).map(
|
|
839
|
+
(key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// packages/utils/src/lib/text-formats/html/table.ts
|
|
844
|
+
function wrap(elem, content) {
|
|
845
|
+
return `<${elem}>${content}</${elem}>${NEW_LINE}`;
|
|
846
|
+
}
|
|
847
|
+
function wrapRow(content) {
|
|
848
|
+
const elem = "tr";
|
|
849
|
+
return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
|
|
850
|
+
}
|
|
851
|
+
function table(tableData) {
|
|
852
|
+
if (tableData.rows.length === 0) {
|
|
853
|
+
throw new Error("Data can't be empty");
|
|
854
|
+
}
|
|
855
|
+
const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
|
|
856
|
+
const tableHeaderRow = wrapRow(tableHeaderCols);
|
|
857
|
+
const tableBody = rowToStringArray(tableData).map((arr) => {
|
|
858
|
+
const columns = arr.map((s) => wrap("td", s)).join("");
|
|
859
|
+
return wrapRow(columns);
|
|
860
|
+
}).join("");
|
|
861
|
+
return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// packages/utils/src/lib/text-formats/md/font-style.ts
|
|
865
|
+
var boldWrap = "**";
|
|
866
|
+
function bold2(text) {
|
|
867
|
+
return `${boldWrap}${text}${boldWrap}`;
|
|
868
|
+
}
|
|
869
|
+
var italicWrap = "_";
|
|
870
|
+
function italic2(text) {
|
|
871
|
+
return `${italicWrap}${text}${italicWrap}`;
|
|
872
|
+
}
|
|
873
|
+
var strikeThroughWrap = "~";
|
|
874
|
+
function strikeThrough(text) {
|
|
875
|
+
return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
|
|
876
|
+
}
|
|
877
|
+
var codeWrap = "`";
|
|
878
|
+
function code2(text) {
|
|
879
|
+
return `${codeWrap}${text}${codeWrap}`;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// packages/utils/src/lib/text-formats/md/headline.ts
|
|
883
|
+
function headline(text, hierarchy = 1) {
|
|
884
|
+
return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
|
|
885
|
+
}
|
|
886
|
+
function h(text, hierarchy = 1) {
|
|
887
|
+
return headline(text, hierarchy);
|
|
888
|
+
}
|
|
889
|
+
function h1(text) {
|
|
890
|
+
return headline(text, 1);
|
|
891
|
+
}
|
|
892
|
+
function h2(text) {
|
|
893
|
+
return headline(text, 2);
|
|
894
|
+
}
|
|
895
|
+
function h3(text) {
|
|
896
|
+
return headline(text, 3);
|
|
897
|
+
}
|
|
898
|
+
function h4(text) {
|
|
899
|
+
return headline(text, 4);
|
|
900
|
+
}
|
|
901
|
+
function h5(text) {
|
|
902
|
+
return headline(text, 5);
|
|
903
|
+
}
|
|
904
|
+
function h6(text) {
|
|
905
|
+
return headline(text, 6);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// packages/utils/src/lib/text-formats/md/image.ts
|
|
909
|
+
function image(src, alt) {
|
|
910
|
+
return ``;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// packages/utils/src/lib/text-formats/md/link.ts
|
|
914
|
+
function link2(href, text) {
|
|
915
|
+
return `[${text || href}](${href})`;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// packages/utils/src/lib/text-formats/md/list.ts
|
|
919
|
+
function li(text, order = "unordered") {
|
|
920
|
+
const style = order === "unordered" ? "-" : "- [ ]";
|
|
921
|
+
return `${style} ${text}`;
|
|
922
|
+
}
|
|
923
|
+
function indentation(text, level = 1) {
|
|
924
|
+
return `${TAB.repeat(level)}${text}`;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// packages/utils/src/lib/text-formats/md/paragraphs.ts
|
|
928
|
+
function paragraphs(...sections) {
|
|
929
|
+
return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// packages/utils/src/lib/text-formats/md/section.ts
|
|
933
|
+
function section(...contents) {
|
|
934
|
+
return `${lines(...contents)}${NEW_LINE}`;
|
|
935
|
+
}
|
|
936
|
+
function lines(...contents) {
|
|
937
|
+
return `${contents.filter(Boolean).join(NEW_LINE)}`;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// packages/utils/src/lib/text-formats/md/table.ts
|
|
941
|
+
var alignString = /* @__PURE__ */ new Map([
|
|
942
|
+
["left", ":--"],
|
|
943
|
+
["center", ":--:"],
|
|
944
|
+
["right", "--:"]
|
|
945
|
+
]);
|
|
946
|
+
function tableRow(rows) {
|
|
947
|
+
return `|${rows.join("|")}|`;
|
|
948
|
+
}
|
|
949
|
+
function table2(data) {
|
|
950
|
+
if (data.rows.length === 0) {
|
|
951
|
+
throw new Error("Data can't be empty");
|
|
952
|
+
}
|
|
953
|
+
const alignmentRow = getColumnAlignments(data).map(
|
|
954
|
+
(s) => alignString.get(s) ?? String(alignString.get("center"))
|
|
955
|
+
);
|
|
956
|
+
return section(
|
|
957
|
+
`${lines(
|
|
958
|
+
tableRow(columnsToStringArray(data)),
|
|
959
|
+
tableRow(alignmentRow),
|
|
960
|
+
...rowToStringArray(data).map(tableRow)
|
|
961
|
+
)}`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// packages/utils/src/lib/text-formats/index.ts
|
|
966
|
+
var md = {
|
|
967
|
+
bold: bold2,
|
|
968
|
+
italic: italic2,
|
|
969
|
+
strikeThrough,
|
|
970
|
+
code: code2,
|
|
971
|
+
link: link2,
|
|
972
|
+
image,
|
|
973
|
+
headline,
|
|
974
|
+
h,
|
|
975
|
+
h1,
|
|
976
|
+
h2,
|
|
977
|
+
h3,
|
|
978
|
+
h4,
|
|
979
|
+
h5,
|
|
980
|
+
h6,
|
|
981
|
+
indentation,
|
|
982
|
+
lines,
|
|
983
|
+
li,
|
|
984
|
+
section,
|
|
985
|
+
paragraphs,
|
|
986
|
+
table: table2
|
|
987
|
+
};
|
|
988
|
+
var html = {
|
|
989
|
+
bold,
|
|
990
|
+
italic,
|
|
991
|
+
code,
|
|
992
|
+
link,
|
|
993
|
+
details,
|
|
994
|
+
table
|
|
995
|
+
};
|
|
996
|
+
|
|
664
997
|
// packages/utils/src/lib/diff.ts
|
|
665
998
|
function matchArrayItemsByKey({
|
|
666
999
|
before,
|
|
@@ -778,40 +1111,48 @@ import chalk from "chalk";
|
|
|
778
1111
|
|
|
779
1112
|
// packages/utils/src/lib/reports/constants.ts
|
|
780
1113
|
var TERMINAL_WIDTH = 80;
|
|
781
|
-
var NEW_LINE = "\n";
|
|
782
1114
|
var SCORE_COLOR_RANGE = {
|
|
783
1115
|
GREEN_MIN: 0.9,
|
|
784
1116
|
YELLOW_MIN: 0.5
|
|
785
1117
|
};
|
|
1118
|
+
var CATEGORIES_TITLE = "\u{1F3F7} Categories";
|
|
786
1119
|
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
787
1120
|
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
788
|
-
var README_LINK = "https://github.com/
|
|
1121
|
+
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
789
1122
|
var reportHeadlineText = "Code PushUp Report";
|
|
790
1123
|
var reportOverviewTableHeaders = [
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1124
|
+
{
|
|
1125
|
+
key: "category",
|
|
1126
|
+
label: "\u{1F3F7} Category",
|
|
1127
|
+
align: "left"
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
key: "score",
|
|
1131
|
+
label: "\u2B50 Score"
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
key: "audits",
|
|
1135
|
+
label: "\u{1F6E1} Audits"
|
|
1136
|
+
}
|
|
794
1137
|
];
|
|
795
1138
|
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
796
|
-
var
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
"Source file",
|
|
814
|
-
"Line(s)"
|
|
1139
|
+
var issuesTableHeadings = [
|
|
1140
|
+
{
|
|
1141
|
+
key: "severity",
|
|
1142
|
+
label: "Severity"
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
key: "message",
|
|
1146
|
+
label: "Message"
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
key: "file",
|
|
1150
|
+
label: "Source file"
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
key: "line",
|
|
1154
|
+
label: "Line(s)"
|
|
1155
|
+
}
|
|
815
1156
|
];
|
|
816
1157
|
|
|
817
1158
|
// packages/utils/src/lib/logging.ts
|
|
@@ -837,7 +1178,7 @@ function logListItem(args) {
|
|
|
837
1178
|
singletonisaacUi.rows = [];
|
|
838
1179
|
singletonUiInstance?.logger.log(content);
|
|
839
1180
|
}
|
|
840
|
-
function
|
|
1181
|
+
function link3(text) {
|
|
841
1182
|
return chalk.underline(chalk.blueBright(text));
|
|
842
1183
|
}
|
|
843
1184
|
|
|
@@ -904,7 +1245,7 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
904
1245
|
await mkdir(baseDir, { recursive: true });
|
|
905
1246
|
return;
|
|
906
1247
|
} catch (error) {
|
|
907
|
-
ui().logger.
|
|
1248
|
+
ui().logger.info(error.message);
|
|
908
1249
|
if (error.code !== "EEXIST") {
|
|
909
1250
|
throw error;
|
|
910
1251
|
}
|
|
@@ -940,122 +1281,35 @@ async function importEsmModule(options2) {
|
|
|
940
1281
|
return mod.default;
|
|
941
1282
|
}
|
|
942
1283
|
|
|
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
|
|
964
|
-
};
|
|
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
|
-
}
|
|
1033
|
-
|
|
1034
1284
|
// packages/utils/src/lib/reports/utils.ts
|
|
1285
|
+
var { image: image2, bold: boldMd } = md;
|
|
1035
1286
|
function formatReportScore(score) {
|
|
1036
1287
|
return Math.round(score * 100).toString();
|
|
1037
1288
|
}
|
|
1038
1289
|
function formatScoreWithColor(score, options2) {
|
|
1039
|
-
const styledNumber = options2?.skipBold ? formatReportScore(score) :
|
|
1040
|
-
return `${
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1290
|
+
const styledNumber = options2?.skipBold ? formatReportScore(score) : boldMd(formatReportScore(score));
|
|
1291
|
+
return `${scoreMarker(score)} ${styledNumber}`;
|
|
1292
|
+
}
|
|
1293
|
+
var MARKERS = {
|
|
1294
|
+
circle: {
|
|
1295
|
+
red: "\u{1F534}",
|
|
1296
|
+
yellow: "\u{1F7E1}",
|
|
1297
|
+
green: "\u{1F7E2}"
|
|
1298
|
+
},
|
|
1299
|
+
square: {
|
|
1300
|
+
red: "\u{1F7E5}",
|
|
1301
|
+
yellow: "\u{1F7E8}",
|
|
1302
|
+
green: "\u{1F7E9}"
|
|
1048
1303
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
function getSquaredScoreMarker(score) {
|
|
1304
|
+
};
|
|
1305
|
+
function scoreMarker(score, markerType = "circle") {
|
|
1052
1306
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1053
|
-
return
|
|
1307
|
+
return MARKERS[markerType].green;
|
|
1054
1308
|
}
|
|
1055
1309
|
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1056
|
-
return
|
|
1310
|
+
return MARKERS[markerType].yellow;
|
|
1057
1311
|
}
|
|
1058
|
-
return
|
|
1312
|
+
return MARKERS[markerType].red;
|
|
1059
1313
|
}
|
|
1060
1314
|
function getDiffMarker(diff) {
|
|
1061
1315
|
if (diff > 0) {
|
|
@@ -1071,7 +1325,7 @@ function colorByScoreDiff(text, diff) {
|
|
|
1071
1325
|
return shieldsBadge(text, color);
|
|
1072
1326
|
}
|
|
1073
1327
|
function shieldsBadge(text, color) {
|
|
1074
|
-
return
|
|
1328
|
+
return image2(
|
|
1075
1329
|
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1076
1330
|
text
|
|
1077
1331
|
);
|
|
@@ -1081,7 +1335,7 @@ function formatDiffNumber(diff) {
|
|
|
1081
1335
|
const sign = diff < 0 ? "\u2212" : "+";
|
|
1082
1336
|
return `${sign}${number}`;
|
|
1083
1337
|
}
|
|
1084
|
-
function
|
|
1338
|
+
function severityMarker(severity) {
|
|
1085
1339
|
if (severity === "error") {
|
|
1086
1340
|
return "\u{1F6A8}";
|
|
1087
1341
|
}
|
|
@@ -1254,12 +1508,12 @@ var ProcessError = class extends Error {
|
|
|
1254
1508
|
}
|
|
1255
1509
|
};
|
|
1256
1510
|
function executeProcess(cfg) {
|
|
1257
|
-
const { observer, cwd, command, args, ignoreExitCode = false } = cfg;
|
|
1511
|
+
const { observer, cwd, command: command2, args, ignoreExitCode = false } = cfg;
|
|
1258
1512
|
const { onStdout, onError, onComplete } = observer ?? {};
|
|
1259
1513
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
1260
1514
|
const start = performance.now();
|
|
1261
1515
|
return new Promise((resolve, reject) => {
|
|
1262
|
-
const process2 = spawn(
|
|
1516
|
+
const process2 = spawn(command2, args, { cwd, shell: true });
|
|
1263
1517
|
let stdout = "";
|
|
1264
1518
|
let stderr = "";
|
|
1265
1519
|
process2.stdout.on("data", (data) => {
|
|
@@ -1272,13 +1526,13 @@ function executeProcess(cfg) {
|
|
|
1272
1526
|
process2.on("error", (err) => {
|
|
1273
1527
|
stderr += err.toString();
|
|
1274
1528
|
});
|
|
1275
|
-
process2.on("close", (
|
|
1529
|
+
process2.on("close", (code3) => {
|
|
1276
1530
|
const timings = { date, duration: calcDuration(start) };
|
|
1277
|
-
if (
|
|
1531
|
+
if (code3 === 0 || ignoreExitCode) {
|
|
1278
1532
|
onComplete?.();
|
|
1279
|
-
resolve({ code, stdout, stderr, ...timings });
|
|
1533
|
+
resolve({ code: code3, stdout, stderr, ...timings });
|
|
1280
1534
|
} else {
|
|
1281
|
-
const errorMsg = new ProcessError({ code, stdout, stderr, ...timings });
|
|
1535
|
+
const errorMsg = new ProcessError({ code: code3, stdout, stderr, ...timings });
|
|
1282
1536
|
onError?.(errorMsg);
|
|
1283
1537
|
reject(errorMsg);
|
|
1284
1538
|
}
|
|
@@ -1294,42 +1548,174 @@ function filterItemRefsBy(items, refFilterFn) {
|
|
|
1294
1548
|
})).filter((item) => item.refs.length);
|
|
1295
1549
|
}
|
|
1296
1550
|
|
|
1297
|
-
// packages/utils/src/lib/git.ts
|
|
1551
|
+
// packages/utils/src/lib/git/git.ts
|
|
1298
1552
|
import { isAbsolute, join as join2, relative } from "node:path";
|
|
1299
1553
|
import { simpleGit } from "simple-git";
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
function toArray(val) {
|
|
1303
|
-
return Array.isArray(val) ? val : [val];
|
|
1554
|
+
function getGitRoot(git = simpleGit()) {
|
|
1555
|
+
return git.revparse("--show-toplevel");
|
|
1304
1556
|
}
|
|
1305
|
-
function
|
|
1306
|
-
|
|
1557
|
+
function formatGitPath(path, gitRoot) {
|
|
1558
|
+
const absolutePath = isAbsolute(path) ? path : join2(process.cwd(), path);
|
|
1559
|
+
const relativePath = relative(gitRoot, absolutePath);
|
|
1560
|
+
return toUnixPath(relativePath);
|
|
1307
1561
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1562
|
+
var GitStatusError = class _GitStatusError extends Error {
|
|
1563
|
+
static ignoredProps = /* @__PURE__ */ new Set(["current", "tracking"]);
|
|
1564
|
+
static getReducedStatus(status) {
|
|
1565
|
+
return Object.fromEntries(
|
|
1566
|
+
Object.entries(status).filter(([key]) => !this.ignoredProps.has(key)).filter(
|
|
1567
|
+
(entry) => {
|
|
1568
|
+
const value = entry[1];
|
|
1569
|
+
if (value == null) {
|
|
1570
|
+
return false;
|
|
1571
|
+
}
|
|
1572
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
if (typeof value === "number" && value === 0) {
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
return !(typeof value === "boolean" && !value);
|
|
1579
|
+
}
|
|
1580
|
+
)
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
constructor(status) {
|
|
1584
|
+
super(
|
|
1585
|
+
`Working directory needs to be clean before we you can proceed. Commit your local changes or stash them:
|
|
1586
|
+
${JSON.stringify(
|
|
1587
|
+
_GitStatusError.getReducedStatus(status),
|
|
1588
|
+
null,
|
|
1589
|
+
2
|
|
1590
|
+
)}`
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
async function guardAgainstLocalChanges(git = simpleGit()) {
|
|
1595
|
+
const status = await git.status(["-s"]);
|
|
1596
|
+
if (status.files.length > 0) {
|
|
1597
|
+
throw new GitStatusError(status);
|
|
1598
|
+
}
|
|
1310
1599
|
}
|
|
1311
|
-
function
|
|
1312
|
-
|
|
1600
|
+
async function safeCheckout(branchOrHash, forceCleanStatus = false, git = simpleGit()) {
|
|
1601
|
+
if (forceCleanStatus) {
|
|
1602
|
+
await git.raw(["reset", "--hard"]);
|
|
1603
|
+
await git.clean(["f", "d"]);
|
|
1604
|
+
ui().logger.info(`git status cleaned`);
|
|
1605
|
+
}
|
|
1606
|
+
await guardAgainstLocalChanges(git);
|
|
1607
|
+
await git.checkout(branchOrHash);
|
|
1313
1608
|
}
|
|
1314
1609
|
|
|
1315
|
-
// packages/utils/src/lib/git.ts
|
|
1316
|
-
|
|
1610
|
+
// packages/utils/src/lib/git/git.commits-and-tags.ts
|
|
1611
|
+
import { simpleGit as simpleGit2 } from "simple-git";
|
|
1612
|
+
|
|
1613
|
+
// packages/utils/src/lib/semver.ts
|
|
1614
|
+
import { rcompare, valid } from "semver";
|
|
1615
|
+
function normalizeSemver(semverString) {
|
|
1616
|
+
if (semverString.startsWith("v") || semverString.startsWith("V")) {
|
|
1617
|
+
return semverString.slice(1);
|
|
1618
|
+
}
|
|
1619
|
+
if (semverString.includes("@")) {
|
|
1620
|
+
return semverString.split("@").at(-1) ?? "";
|
|
1621
|
+
}
|
|
1622
|
+
return semverString;
|
|
1623
|
+
}
|
|
1624
|
+
function isSemver(semverString = "") {
|
|
1625
|
+
return valid(normalizeSemver(semverString)) != null;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
// packages/utils/src/lib/git/git.commits-and-tags.ts
|
|
1629
|
+
async function getLatestCommit(git = simpleGit2()) {
|
|
1317
1630
|
const log2 = await git.log({
|
|
1318
1631
|
maxCount: 1,
|
|
1632
|
+
// git log -1 --pretty=format:"%H %s %an %aI" - See: https://git-scm.com/docs/pretty-formats
|
|
1319
1633
|
format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
|
|
1320
1634
|
});
|
|
1321
|
-
if (!log2.latest) {
|
|
1322
|
-
return null;
|
|
1323
|
-
}
|
|
1324
1635
|
return commitSchema.parse(log2.latest);
|
|
1325
1636
|
}
|
|
1326
|
-
function
|
|
1327
|
-
return git.
|
|
1637
|
+
async function getCurrentBranchOrTag(git = simpleGit2()) {
|
|
1638
|
+
return await git.branch().then((r) => r.current) || // If no current branch, try to get the tag
|
|
1639
|
+
// @TODO use simple git
|
|
1640
|
+
await git.raw(["describe", "--tags", "--exact-match"]).then((out) => out.trim());
|
|
1328
1641
|
}
|
|
1329
|
-
function
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1642
|
+
function validateFilter({ from, to }) {
|
|
1643
|
+
if (to && !from) {
|
|
1644
|
+
throw new Error(
|
|
1645
|
+
`filter needs the "from" option defined to accept the "to" option.
|
|
1646
|
+
`
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
function filterLogs(allTags, opt) {
|
|
1651
|
+
if (!opt) {
|
|
1652
|
+
return allTags;
|
|
1653
|
+
}
|
|
1654
|
+
validateFilter(opt);
|
|
1655
|
+
const { from, to, maxCount } = opt;
|
|
1656
|
+
const finIndex = (tagName, fallback) => {
|
|
1657
|
+
const idx = allTags.indexOf(tagName ?? "");
|
|
1658
|
+
if (idx > -1) {
|
|
1659
|
+
return idx;
|
|
1660
|
+
}
|
|
1661
|
+
return fallback;
|
|
1662
|
+
};
|
|
1663
|
+
const fromIndex = finIndex(from, 0);
|
|
1664
|
+
const toIndex = finIndex(to, void 0);
|
|
1665
|
+
return allTags.slice(fromIndex, toIndex ? toIndex + 1 : toIndex).slice(0, maxCount ?? void 0);
|
|
1666
|
+
}
|
|
1667
|
+
async function getHashFromTag(tag, git = simpleGit2()) {
|
|
1668
|
+
const tagDetails = await git.show(["--no-patch", "--format=%H", tag]);
|
|
1669
|
+
const hash = tagDetails.trim();
|
|
1670
|
+
return {
|
|
1671
|
+
hash: hash.split("\n").at(-1) ?? "",
|
|
1672
|
+
message: tag
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
async function getSemverTags(opt = {}, git = simpleGit2()) {
|
|
1676
|
+
validateFilter(opt);
|
|
1677
|
+
const { targetBranch, ...options2 } = opt;
|
|
1678
|
+
let currentBranch;
|
|
1679
|
+
if (targetBranch) {
|
|
1680
|
+
currentBranch = await getCurrentBranchOrTag(git);
|
|
1681
|
+
await git.checkout(targetBranch);
|
|
1682
|
+
}
|
|
1683
|
+
const tagsRaw = await git.tag([
|
|
1684
|
+
"--merged",
|
|
1685
|
+
targetBranch ?? await getCurrentBranchOrTag(git)
|
|
1686
|
+
]);
|
|
1687
|
+
const allTags = tagsRaw.split(/\n/).map((tag) => tag.trim()).filter(Boolean).filter(isSemver);
|
|
1688
|
+
const relevantTags = filterLogs(allTags, options2);
|
|
1689
|
+
const tagsWithHashes = await Promise.all(
|
|
1690
|
+
relevantTags.map((tag) => getHashFromTag(tag, git))
|
|
1691
|
+
);
|
|
1692
|
+
if (currentBranch) {
|
|
1693
|
+
await git.checkout(currentBranch);
|
|
1694
|
+
}
|
|
1695
|
+
return tagsWithHashes;
|
|
1696
|
+
}
|
|
1697
|
+
async function getHashes(options2 = {}, git = simpleGit2()) {
|
|
1698
|
+
const { targetBranch, from, to, maxCount, ...opt } = options2;
|
|
1699
|
+
validateFilter({ from, to });
|
|
1700
|
+
let currentBranch;
|
|
1701
|
+
if (targetBranch) {
|
|
1702
|
+
currentBranch = await getCurrentBranchOrTag(git);
|
|
1703
|
+
await git.checkout(targetBranch);
|
|
1704
|
+
}
|
|
1705
|
+
const logs = await git.log({
|
|
1706
|
+
...opt,
|
|
1707
|
+
format: {
|
|
1708
|
+
hash: "%H",
|
|
1709
|
+
message: "%s"
|
|
1710
|
+
},
|
|
1711
|
+
from,
|
|
1712
|
+
to,
|
|
1713
|
+
maxCount
|
|
1714
|
+
});
|
|
1715
|
+
if (targetBranch) {
|
|
1716
|
+
await git.checkout(currentBranch);
|
|
1717
|
+
}
|
|
1718
|
+
return [...logs.all];
|
|
1333
1719
|
}
|
|
1334
1720
|
|
|
1335
1721
|
// packages/utils/src/lib/group-by-status.ts
|
|
@@ -1407,44 +1793,65 @@ function listAuditsFromAllPlugins(report) {
|
|
|
1407
1793
|
);
|
|
1408
1794
|
}
|
|
1409
1795
|
|
|
1410
|
-
// packages/utils/src/lib/reports/
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
`${FOOTER_PREFIX} ${link2(README_LINK, "Code PushUp")}`
|
|
1796
|
+
// packages/utils/src/lib/reports/formatting.ts
|
|
1797
|
+
var { headline: headline2, lines: lines2, link: link4, section: section2, table: table3 } = md;
|
|
1798
|
+
function tableSection(tableData, options2) {
|
|
1799
|
+
if (tableData.rows.length === 0) {
|
|
1800
|
+
return "";
|
|
1801
|
+
}
|
|
1802
|
+
const { level = 4 } = options2 ?? {};
|
|
1803
|
+
const render = (h7, l) => l === 0 ? h7 : headline2(h7, l);
|
|
1804
|
+
return lines2(
|
|
1805
|
+
tableData.title && render(tableData.title, level),
|
|
1806
|
+
table3(tableData)
|
|
1422
1807
|
);
|
|
1423
1808
|
}
|
|
1424
|
-
function
|
|
1425
|
-
|
|
1809
|
+
function metaDescription({
|
|
1810
|
+
docsUrl,
|
|
1811
|
+
description
|
|
1812
|
+
}) {
|
|
1813
|
+
if (docsUrl) {
|
|
1814
|
+
const docsLink = link4(docsUrl, "\u{1F4D6} Docs");
|
|
1815
|
+
if (!description) {
|
|
1816
|
+
return section2(docsLink);
|
|
1817
|
+
}
|
|
1818
|
+
const parsedDescription = description.toString().endsWith("```") ? `${description}${NEW_LINE + NEW_LINE}` : `${description}${SPACE}`;
|
|
1819
|
+
return section2(`${parsedDescription}${docsLink}`);
|
|
1820
|
+
}
|
|
1821
|
+
if (description && description.trim().length > 0) {
|
|
1822
|
+
return section2(description);
|
|
1823
|
+
}
|
|
1824
|
+
return "";
|
|
1426
1825
|
}
|
|
1427
|
-
|
|
1826
|
+
|
|
1827
|
+
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
1828
|
+
var { link: link5, section: section3, h2: h22, lines: lines3, li: li2, bold: boldMd2, h3: h32, indentation: indentation2 } = md;
|
|
1829
|
+
function categoriesOverviewSection(report) {
|
|
1428
1830
|
const { categories, plugins } = report;
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1831
|
+
if (categories.length > 0 && plugins.length > 0) {
|
|
1832
|
+
const tableContent = {
|
|
1833
|
+
columns: reportOverviewTableHeaders,
|
|
1834
|
+
rows: categories.map(({ title, refs, score }) => ({
|
|
1835
|
+
// The heading "ID" is inferred from the heading text in Markdown.
|
|
1836
|
+
category: link5(`#${slugify(title)}`, title),
|
|
1837
|
+
score: `${scoreMarker(score)}${SPACE}${boldMd2(
|
|
1838
|
+
formatReportScore(score)
|
|
1839
|
+
)}`,
|
|
1840
|
+
audits: countCategoryAudits(refs, plugins).toString()
|
|
1841
|
+
}))
|
|
1842
|
+
};
|
|
1843
|
+
return tableSection(tableContent);
|
|
1844
|
+
}
|
|
1845
|
+
return "";
|
|
1438
1846
|
}
|
|
1439
|
-
function
|
|
1847
|
+
function categoriesDetailsSection(report) {
|
|
1440
1848
|
const { categories, plugins } = report;
|
|
1441
|
-
const categoryDetails = categories.
|
|
1442
|
-
const categoryTitle =
|
|
1443
|
-
const categoryScore = `${
|
|
1849
|
+
const categoryDetails = categories.flatMap((category) => {
|
|
1850
|
+
const categoryTitle = h32(category.title);
|
|
1851
|
+
const categoryScore = `${scoreMarker(
|
|
1444
1852
|
category.score
|
|
1445
|
-
)}
|
|
1446
|
-
const
|
|
1447
|
-
const categoryMDItems = category.refs.reduce((refAcc, ref) => {
|
|
1853
|
+
)}${SPACE}Score: ${boldMd2(formatReportScore(category.score))}`;
|
|
1854
|
+
const categoryMDItems = category.refs.map((ref) => {
|
|
1448
1855
|
if (ref.type === "group") {
|
|
1449
1856
|
const group = getSortableGroupByRef(ref, plugins);
|
|
1450
1857
|
const groupAudits = group.refs.map(
|
|
@@ -1454,151 +1861,240 @@ function reportToCategoriesSection(report) {
|
|
|
1454
1861
|
)
|
|
1455
1862
|
);
|
|
1456
1863
|
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1457
|
-
|
|
1458
|
-
group,
|
|
1459
|
-
groupAudits,
|
|
1460
|
-
pluginTitle
|
|
1461
|
-
);
|
|
1462
|
-
return refAcc + mdGroupItem + NEW_LINE;
|
|
1864
|
+
return categoryGroupItem(group, groupAudits, pluginTitle);
|
|
1463
1865
|
} else {
|
|
1464
1866
|
const audit = getSortableAuditByRef(ref, plugins);
|
|
1465
1867
|
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1466
|
-
|
|
1467
|
-
return refAcc + mdAuditItem + NEW_LINE;
|
|
1868
|
+
return categoryRef(audit, pluginTitle);
|
|
1468
1869
|
}
|
|
1469
|
-
}
|
|
1470
|
-
return
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
const auditTitle = link2(
|
|
1476
|
-
`#${slugify(audit.title)}-${slugify(pluginTitle)}`,
|
|
1477
|
-
audit.title
|
|
1478
|
-
);
|
|
1479
|
-
return li(
|
|
1480
|
-
`${getSquaredScoreMarker(
|
|
1481
|
-
audit.score
|
|
1482
|
-
)} ${auditTitle} (_${pluginTitle}_) - ${getAuditResult(audit)}`
|
|
1483
|
-
);
|
|
1484
|
-
}
|
|
1485
|
-
function groupItemToCategorySection(group, groupAudits, pluginTitle) {
|
|
1486
|
-
const groupScore = Number(formatReportScore(group.score || 0));
|
|
1487
|
-
const groupTitle = li(
|
|
1488
|
-
`${getRoundScoreMarker(groupScore)} ${group.title} (_${pluginTitle}_)`
|
|
1489
|
-
);
|
|
1490
|
-
const auditTitles = groupAudits.reduce((acc, audit) => {
|
|
1491
|
-
const auditTitle = link2(
|
|
1492
|
-
`#${slugify(audit.title)}-${slugify(pluginTitle)}`,
|
|
1493
|
-
audit.title
|
|
1870
|
+
});
|
|
1871
|
+
return section3(
|
|
1872
|
+
categoryTitle,
|
|
1873
|
+
metaDescription(category),
|
|
1874
|
+
categoryScore,
|
|
1875
|
+
...categoryMDItems
|
|
1494
1876
|
);
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1877
|
+
});
|
|
1878
|
+
return lines3(h22(CATEGORIES_TITLE), ...categoryDetails);
|
|
1879
|
+
}
|
|
1880
|
+
function categoryRef({ title, score, value, displayValue }, pluginTitle) {
|
|
1881
|
+
const auditTitleAsLink = link5(
|
|
1882
|
+
`#${slugify(title)}-${slugify(pluginTitle)}`,
|
|
1883
|
+
title
|
|
1884
|
+
);
|
|
1885
|
+
const marker = scoreMarker(score, "square");
|
|
1886
|
+
return li2(
|
|
1887
|
+
`${marker}${SPACE}${auditTitleAsLink}${SPACE}(_${pluginTitle}_) - ${boldMd2(
|
|
1888
|
+
(displayValue || value).toString()
|
|
1889
|
+
)}`
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
function categoryGroupItem({ score = 0, title }, groupAudits, pluginTitle) {
|
|
1893
|
+
const groupTitle = li2(
|
|
1894
|
+
`${scoreMarker(score)}${SPACE}${title}${SPACE}(_${pluginTitle}_)`
|
|
1895
|
+
);
|
|
1896
|
+
const auditTitles = groupAudits.map(
|
|
1897
|
+
({ title: auditTitle, score: auditScore, value, displayValue }) => {
|
|
1898
|
+
const auditTitleLink = link5(
|
|
1899
|
+
`#${slugify(auditTitle)}-${slugify(pluginTitle)}`,
|
|
1900
|
+
auditTitle
|
|
1901
|
+
);
|
|
1902
|
+
const marker = scoreMarker(auditScore, "square");
|
|
1903
|
+
return indentation2(
|
|
1904
|
+
li2(
|
|
1905
|
+
`${marker}${SPACE}${auditTitleLink} - ${boldMd2(
|
|
1906
|
+
String(displayValue ?? value)
|
|
1907
|
+
)}`
|
|
1908
|
+
)
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
);
|
|
1912
|
+
return lines3(groupTitle, ...auditTitles);
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
1916
|
+
var { h1: h12, h2: h23, h3: h33, lines: lines4, link: link6, section: section4, code: codeMd } = md;
|
|
1917
|
+
var { bold: boldHtml, details: details2 } = html;
|
|
1918
|
+
function auditDetailsAuditValue({
|
|
1919
|
+
score,
|
|
1920
|
+
value,
|
|
1921
|
+
displayValue
|
|
1922
|
+
}) {
|
|
1923
|
+
return `${scoreMarker(score, "square")} ${boldHtml(
|
|
1924
|
+
String(displayValue ?? value)
|
|
1925
|
+
)} (score: ${formatReportScore(score)})`;
|
|
1926
|
+
}
|
|
1927
|
+
function generateMdReport(report) {
|
|
1928
|
+
const printCategories = report.categories.length > 0;
|
|
1929
|
+
return lines4(
|
|
1930
|
+
h12(reportHeadlineText),
|
|
1931
|
+
printCategories ? categoriesOverviewSection(report) : "",
|
|
1932
|
+
printCategories ? categoriesDetailsSection(report) : "",
|
|
1933
|
+
auditsSection(report),
|
|
1934
|
+
aboutSection(report),
|
|
1935
|
+
`${FOOTER_PREFIX}${SPACE}${link6(README_LINK, "Code PushUp")}`
|
|
1936
|
+
);
|
|
1937
|
+
}
|
|
1938
|
+
function auditDetailsIssues(issues = []) {
|
|
1939
|
+
if (issues.length === 0) {
|
|
1940
|
+
return "";
|
|
1941
|
+
}
|
|
1942
|
+
const detailsTableData = {
|
|
1943
|
+
title: "Issues",
|
|
1944
|
+
columns: issuesTableHeadings,
|
|
1945
|
+
rows: issues.map(
|
|
1946
|
+
({ severity: severityVal, message, source: sourceVal }) => {
|
|
1947
|
+
const severity = `${severityMarker(severityVal)} <i>${severityVal}</i>`;
|
|
1948
|
+
if (!sourceVal) {
|
|
1949
|
+
return { severity, message, file: "", line: "" };
|
|
1950
|
+
}
|
|
1951
|
+
const file = `<code>${sourceVal.file}</code>`;
|
|
1952
|
+
if (!sourceVal.position) {
|
|
1953
|
+
return { severity, message, file, line: "" };
|
|
1954
|
+
}
|
|
1955
|
+
const { startLine, endLine } = sourceVal.position;
|
|
1956
|
+
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1957
|
+
return { severity, message, file, line };
|
|
1958
|
+
}
|
|
1959
|
+
)
|
|
1960
|
+
};
|
|
1961
|
+
return tableSection(detailsTableData);
|
|
1962
|
+
}
|
|
1963
|
+
function auditDetails(audit) {
|
|
1964
|
+
const { table: table5, issues = [] } = audit.details ?? {};
|
|
1965
|
+
const detailsValue = auditDetailsAuditValue(audit);
|
|
1966
|
+
if (issues.length === 0 && table5 == null) {
|
|
1967
|
+
return section4(detailsValue);
|
|
1968
|
+
}
|
|
1969
|
+
const tableSectionContent = table5 == null ? "" : tableSection(table5);
|
|
1970
|
+
const issuesSectionContent = issues.length > 0 ? auditDetailsIssues(issues) : "";
|
|
1971
|
+
return details2(
|
|
1972
|
+
detailsValue,
|
|
1973
|
+
lines4(tableSectionContent, issuesSectionContent)
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1976
|
+
function auditsSection({
|
|
1977
|
+
plugins
|
|
1978
|
+
}) {
|
|
1979
|
+
const content = plugins.flatMap(
|
|
1980
|
+
({ slug, audits }) => audits.flatMap((audit) => {
|
|
1981
|
+
const auditTitle = `${audit.title}${SPACE}(${getPluginNameFromSlug(
|
|
1982
|
+
slug,
|
|
1983
|
+
plugins
|
|
1509
1984
|
)})`;
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1985
|
+
const detailsContent = auditDetails(audit);
|
|
1986
|
+
const descriptionContent = metaDescription(audit);
|
|
1987
|
+
return [h33(auditTitle), detailsContent, descriptionContent];
|
|
1988
|
+
})
|
|
1989
|
+
);
|
|
1990
|
+
return section4(h23("\u{1F6E1}\uFE0F Audits"), ...content);
|
|
1991
|
+
}
|
|
1992
|
+
function aboutSection(report) {
|
|
1993
|
+
const { date, plugins } = report;
|
|
1994
|
+
const reportMetaTable = reportMetaData(report);
|
|
1995
|
+
const pluginMetaTable = reportPluginMeta({ plugins });
|
|
1996
|
+
return lines4(
|
|
1997
|
+
h23("About"),
|
|
1998
|
+
section4(
|
|
1999
|
+
`Report was created by [Code PushUp](${README_LINK}) on ${formatDate(
|
|
2000
|
+
new Date(date)
|
|
2001
|
+
)}.`
|
|
2002
|
+
),
|
|
2003
|
+
tableSection(pluginMetaTable),
|
|
2004
|
+
tableSection(reportMetaTable)
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
function reportPluginMeta({ plugins }) {
|
|
2008
|
+
return {
|
|
2009
|
+
columns: [
|
|
2010
|
+
{
|
|
2011
|
+
key: "plugin",
|
|
2012
|
+
align: "left"
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
key: "audits"
|
|
2016
|
+
},
|
|
2017
|
+
{
|
|
2018
|
+
key: "version"
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
key: "duration"
|
|
2022
|
+
}
|
|
2023
|
+
],
|
|
2024
|
+
rows: plugins.map(
|
|
2025
|
+
({
|
|
2026
|
+
title: pluginTitle,
|
|
2027
|
+
audits,
|
|
2028
|
+
version: pluginVersion,
|
|
2029
|
+
duration: pluginDuration
|
|
2030
|
+
}) => ({
|
|
2031
|
+
plugin: pluginTitle,
|
|
2032
|
+
audits: audits.length.toString(),
|
|
2033
|
+
version: codeMd(pluginVersion || ""),
|
|
2034
|
+
duration: formatDuration(pluginDuration)
|
|
2035
|
+
})
|
|
2036
|
+
)
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
function reportMetaData({
|
|
2040
|
+
commit,
|
|
2041
|
+
version: version2,
|
|
2042
|
+
duration,
|
|
2043
|
+
plugins,
|
|
2044
|
+
categories
|
|
2045
|
+
}) {
|
|
2046
|
+
const commitInfo = commit ? `${commit.message}${SPACE}(${commit.hash})` : "N/A";
|
|
2047
|
+
return {
|
|
2048
|
+
columns: [
|
|
2049
|
+
{
|
|
2050
|
+
key: "commit",
|
|
2051
|
+
align: "left"
|
|
2052
|
+
},
|
|
2053
|
+
{
|
|
2054
|
+
key: "version"
|
|
2055
|
+
},
|
|
2056
|
+
{
|
|
2057
|
+
key: "duration"
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
key: "plugins"
|
|
2061
|
+
},
|
|
2062
|
+
{
|
|
2063
|
+
key: "categories"
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
key: "audits"
|
|
1531
2067
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2068
|
+
],
|
|
2069
|
+
rows: [
|
|
2070
|
+
{
|
|
2071
|
+
commit: commitInfo,
|
|
2072
|
+
version: codeMd(version2 || ""),
|
|
2073
|
+
duration: formatDuration(duration),
|
|
2074
|
+
plugins: plugins.length,
|
|
2075
|
+
categories: categories.length,
|
|
2076
|
+
audits: plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
|
|
1535
2077
|
}
|
|
1536
|
-
const { startLine, endLine } = issue.source.position;
|
|
1537
|
-
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1538
|
-
return [severity, message, file, line];
|
|
1539
|
-
})
|
|
1540
|
-
];
|
|
1541
|
-
const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
|
|
1542
|
-
return details(detailsTitle, detailsTable);
|
|
1543
|
-
}
|
|
1544
|
-
function reportToAboutSection(report) {
|
|
1545
|
-
const date = formatDate(/* @__PURE__ */ new Date());
|
|
1546
|
-
const { duration, version: version2, commit, plugins, categories } = report;
|
|
1547
|
-
const commitInfo = commit ? `${commit.message} (${commit.hash})` : "N/A";
|
|
1548
|
-
const reportMetaTable = [
|
|
1549
|
-
reportMetaTableHeaders,
|
|
1550
|
-
[
|
|
1551
|
-
commitInfo,
|
|
1552
|
-
style(version2 || "", ["c"]),
|
|
1553
|
-
formatDuration(duration),
|
|
1554
|
-
plugins.length.toString(),
|
|
1555
|
-
categories.length.toString(),
|
|
1556
|
-
plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
|
|
1557
2078
|
]
|
|
1558
|
-
|
|
1559
|
-
const pluginMetaTable = [
|
|
1560
|
-
pluginMetaTableHeaders,
|
|
1561
|
-
...plugins.map((plugin) => [
|
|
1562
|
-
plugin.title,
|
|
1563
|
-
plugin.audits.length.toString(),
|
|
1564
|
-
style(plugin.version || "", ["c"]),
|
|
1565
|
-
formatDuration(plugin.duration)
|
|
1566
|
-
])
|
|
1567
|
-
];
|
|
1568
|
-
return (
|
|
1569
|
-
// eslint-disable-next-line prefer-template
|
|
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"])
|
|
1571
|
-
);
|
|
1572
|
-
}
|
|
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));
|
|
2079
|
+
};
|
|
1595
2080
|
}
|
|
1596
2081
|
|
|
1597
2082
|
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
2083
|
+
var {
|
|
2084
|
+
h1: h13,
|
|
2085
|
+
h2: h24,
|
|
2086
|
+
lines: lines5,
|
|
2087
|
+
link: link7,
|
|
2088
|
+
bold: boldMd3,
|
|
2089
|
+
italic: italicMd,
|
|
2090
|
+
table: table4,
|
|
2091
|
+
section: section5
|
|
2092
|
+
} = md;
|
|
2093
|
+
var { details: details3 } = html;
|
|
1598
2094
|
var MAX_ROWS = 100;
|
|
1599
2095
|
function generateMdReportsDiff(diff) {
|
|
1600
|
-
return
|
|
1601
|
-
formatDiffHeaderSection(diff),
|
|
2096
|
+
return lines5(
|
|
2097
|
+
section5(formatDiffHeaderSection(diff)),
|
|
1602
2098
|
formatDiffCategoriesSection(diff),
|
|
1603
2099
|
formatDiffGroupsSection(diff),
|
|
1604
2100
|
formatDiffAuditsSection(diff)
|
|
@@ -1606,12 +2102,12 @@ function generateMdReportsDiff(diff) {
|
|
|
1606
2102
|
}
|
|
1607
2103
|
function formatDiffHeaderSection(diff) {
|
|
1608
2104
|
const outcomeTexts = {
|
|
1609
|
-
positive: `\u{1F973} Code PushUp report has ${
|
|
1610
|
-
negative: `\u{1F61F} Code PushUp report has ${
|
|
1611
|
-
mixed: `\u{1F928} Code PushUp report has both ${
|
|
2105
|
+
positive: `\u{1F973} Code PushUp report has ${boldMd3("improved")}`,
|
|
2106
|
+
negative: `\u{1F61F} Code PushUp report has ${boldMd3("regressed")}`,
|
|
2107
|
+
mixed: `\u{1F928} Code PushUp report has both ${boldMd3(
|
|
1612
2108
|
"improvements and regressions"
|
|
1613
2109
|
)}`,
|
|
1614
|
-
unchanged: `\u{1F610} Code PushUp report is ${
|
|
2110
|
+
unchanged: `\u{1F610} Code PushUp report is ${boldMd3("unchanged")}`
|
|
1615
2111
|
};
|
|
1616
2112
|
const outcome = mergeDiffOutcomes(
|
|
1617
2113
|
changesToDiffOutcomes([
|
|
@@ -1621,8 +2117,8 @@ function formatDiffHeaderSection(diff) {
|
|
|
1621
2117
|
])
|
|
1622
2118
|
);
|
|
1623
2119
|
const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
1624
|
-
return
|
|
1625
|
-
|
|
2120
|
+
return lines5(
|
|
2121
|
+
h13("Code PushUp"),
|
|
1626
2122
|
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1627
2123
|
);
|
|
1628
2124
|
}
|
|
@@ -1633,102 +2129,104 @@ function formatDiffCategoriesSection(diff) {
|
|
|
1633
2129
|
if (categoriesCount === 0) {
|
|
1634
2130
|
return "";
|
|
1635
2131
|
}
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
formatScoreWithColor(category.scores.
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
category.
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
2132
|
+
const columns = [
|
|
2133
|
+
{ key: "category", label: "\u{1F3F7}\uFE0F Category", align: "left" },
|
|
2134
|
+
{ key: "after", label: hasChanges ? "\u2B50 Current score" : "\u2B50 Score" },
|
|
2135
|
+
{ key: "before", label: "\u2B50 Previous score" },
|
|
2136
|
+
{ key: "change", label: "\u{1F504} Score change" }
|
|
2137
|
+
];
|
|
2138
|
+
return lines5(
|
|
2139
|
+
h24("\u{1F3F7}\uFE0F Categories"),
|
|
2140
|
+
categoriesCount > 0 && table4({
|
|
2141
|
+
columns: hasChanges ? columns : columns.slice(0, 2),
|
|
2142
|
+
rows: [
|
|
2143
|
+
...sortChanges(changed).map((category) => ({
|
|
2144
|
+
category: formatTitle(category),
|
|
2145
|
+
after: formatScoreWithColor(category.scores.after),
|
|
2146
|
+
before: formatScoreWithColor(category.scores.before, {
|
|
2147
|
+
skipBold: true
|
|
2148
|
+
}),
|
|
2149
|
+
change: formatScoreChange(category.scores.diff)
|
|
2150
|
+
})),
|
|
2151
|
+
...added.map((category) => ({
|
|
2152
|
+
category: formatTitle(category),
|
|
2153
|
+
after: formatScoreWithColor(category.score),
|
|
2154
|
+
before: italicMd("n/a (\\*)"),
|
|
2155
|
+
change: italicMd("n/a (\\*)")
|
|
2156
|
+
})),
|
|
2157
|
+
...unchanged.map((category) => ({
|
|
2158
|
+
category: formatTitle(category),
|
|
2159
|
+
after: formatScoreWithColor(category.score),
|
|
2160
|
+
before: formatScoreWithColor(category.score, { skipBold: true }),
|
|
2161
|
+
change: "\u2013"
|
|
2162
|
+
}))
|
|
2163
|
+
].map(
|
|
2164
|
+
(row) => hasChanges ? row : { category: row.category, after: row.after }
|
|
2165
|
+
)
|
|
2166
|
+
}),
|
|
2167
|
+
added.length > 0 && section5(italicMd("(\\*) New category."))
|
|
1668
2168
|
);
|
|
1669
2169
|
}
|
|
1670
2170
|
function formatDiffGroupsSection(diff) {
|
|
1671
2171
|
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1672
2172
|
return "";
|
|
1673
2173
|
}
|
|
1674
|
-
return
|
|
1675
|
-
|
|
2174
|
+
return lines5(
|
|
2175
|
+
h24("\u{1F5C3}\uFE0F Groups"),
|
|
1676
2176
|
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1677
|
-
|
|
1678
|
-
"\u{1F50C} Plugin",
|
|
1679
|
-
"\u{1F5C3}\uFE0F Group",
|
|
1680
|
-
"\u2B50 Current score",
|
|
1681
|
-
"\u2B50 Previous score",
|
|
1682
|
-
"\u{1F504} Score change"
|
|
2177
|
+
columns: [
|
|
2178
|
+
{ key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
|
|
2179
|
+
{ key: "group", label: "\u{1F5C3}\uFE0F Group", align: "left" },
|
|
2180
|
+
{ key: "after", label: "\u2B50 Current score" },
|
|
2181
|
+
{ key: "before", label: "\u2B50 Previous score" },
|
|
2182
|
+
{ key: "change", label: "\u{1F504} Score change" }
|
|
1683
2183
|
],
|
|
1684
|
-
rows: sortChanges(diff.groups.changed).map((group) =>
|
|
1685
|
-
group.plugin
|
|
1686
|
-
group
|
|
1687
|
-
formatScoreWithColor(group.scores.after),
|
|
1688
|
-
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1689
|
-
formatScoreChange(group.scores.diff)
|
|
1690
|
-
|
|
1691
|
-
align: ["l", "l", "c", "c", "c"]
|
|
2184
|
+
rows: sortChanges(diff.groups.changed).map((group) => ({
|
|
2185
|
+
plugin: formatTitle(group.plugin),
|
|
2186
|
+
group: formatTitle(group),
|
|
2187
|
+
after: formatScoreWithColor(group.scores.after),
|
|
2188
|
+
before: formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
2189
|
+
change: formatScoreChange(group.scores.diff)
|
|
2190
|
+
}))
|
|
1692
2191
|
})
|
|
1693
2192
|
);
|
|
1694
2193
|
}
|
|
1695
2194
|
function formatDiffAuditsSection(diff) {
|
|
1696
|
-
return
|
|
1697
|
-
|
|
2195
|
+
return lines5(
|
|
2196
|
+
h24("\u{1F6E1}\uFE0F Audits"),
|
|
1698
2197
|
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1699
|
-
|
|
1700
|
-
"\u{1F50C} Plugin",
|
|
1701
|
-
"\u{1F6E1}\uFE0F Audit",
|
|
1702
|
-
"\u{1F4CF} Current value",
|
|
1703
|
-
"\u{1F4CF} Previous value",
|
|
1704
|
-
"\u{1F504} Value change"
|
|
2198
|
+
columns: [
|
|
2199
|
+
{ key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
|
|
2200
|
+
{ key: "audit", label: "\u{1F6E1}\uFE0F Audit", align: "left" },
|
|
2201
|
+
{ key: "after", label: "\u{1F4CF} Current value" },
|
|
2202
|
+
{ key: "before", label: "\u{1F4CF} Previous value" },
|
|
2203
|
+
{ key: "change", label: "\u{1F504} Value change" }
|
|
1705
2204
|
],
|
|
1706
|
-
rows: sortChanges(diff.audits.changed).map((audit) =>
|
|
1707
|
-
audit.plugin
|
|
1708
|
-
audit
|
|
1709
|
-
`${
|
|
2205
|
+
rows: sortChanges(diff.audits.changed).map((audit) => ({
|
|
2206
|
+
plugin: formatTitle(audit.plugin),
|
|
2207
|
+
audit: formatTitle(audit),
|
|
2208
|
+
after: `${scoreMarker(audit.scores.after, "square")} ${boldMd3(
|
|
1710
2209
|
audit.displayValues.after || audit.values.after.toString()
|
|
1711
2210
|
)}`,
|
|
1712
|
-
`${
|
|
1713
|
-
formatValueChange(audit)
|
|
1714
|
-
|
|
1715
|
-
align: ["l", "l", "c", "c", "c"]
|
|
2211
|
+
before: `${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
2212
|
+
change: formatValueChange(audit)
|
|
2213
|
+
}))
|
|
1716
2214
|
})
|
|
1717
2215
|
);
|
|
1718
2216
|
}
|
|
1719
|
-
function formatGroupsOrAuditsDetails(token, { changed, unchanged },
|
|
1720
|
-
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) :
|
|
2217
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, tableData) {
|
|
2218
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details3(
|
|
1721
2219
|
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
2220
|
+
lines5(
|
|
2221
|
+
table4({
|
|
2222
|
+
...tableData,
|
|
2223
|
+
rows: tableData.rows.slice(0, MAX_ROWS)
|
|
2224
|
+
// use never to avoid typing problem
|
|
2225
|
+
}),
|
|
2226
|
+
changed.length > MAX_ROWS && italicMd(
|
|
1728
2227
|
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1729
2228
|
token
|
|
1730
|
-
)} are listed above for brevity
|
|
1731
|
-
["i"]
|
|
2229
|
+
)} are listed above for brevity.`
|
|
1732
2230
|
),
|
|
1733
2231
|
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1734
2232
|
)
|
|
@@ -1749,11 +2247,13 @@ function formatValueChange({
|
|
|
1749
2247
|
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1750
2248
|
}
|
|
1751
2249
|
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1752
|
-
return
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
2250
|
+
return section5(
|
|
2251
|
+
[
|
|
2252
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
2253
|
+
unchanged.length === 1 ? "is" : "are",
|
|
2254
|
+
"unchanged."
|
|
2255
|
+
].join(" ")
|
|
2256
|
+
);
|
|
1757
2257
|
}
|
|
1758
2258
|
function summarizeDiffOutcomes(outcomes, token) {
|
|
1759
2259
|
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
@@ -1773,6 +2273,15 @@ function summarizeDiffOutcomes(outcomes, token) {
|
|
|
1773
2273
|
}
|
|
1774
2274
|
}).join(", ");
|
|
1775
2275
|
}
|
|
2276
|
+
function formatTitle({
|
|
2277
|
+
title,
|
|
2278
|
+
docsUrl
|
|
2279
|
+
}) {
|
|
2280
|
+
if (docsUrl) {
|
|
2281
|
+
return link7(docsUrl, title);
|
|
2282
|
+
}
|
|
2283
|
+
return title;
|
|
2284
|
+
}
|
|
1776
2285
|
function sortChanges(changes) {
|
|
1777
2286
|
return [...changes].sort(
|
|
1778
2287
|
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
@@ -1820,7 +2329,7 @@ function log(msg = "") {
|
|
|
1820
2329
|
}
|
|
1821
2330
|
function logStdoutSummary(report) {
|
|
1822
2331
|
const printCategories = report.categories.length > 0;
|
|
1823
|
-
log(
|
|
2332
|
+
log(reportToHeaderSection(report));
|
|
1824
2333
|
log();
|
|
1825
2334
|
logPlugins(report);
|
|
1826
2335
|
if (printCategories) {
|
|
@@ -1829,7 +2338,7 @@ function logStdoutSummary(report) {
|
|
|
1829
2338
|
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1830
2339
|
log();
|
|
1831
2340
|
}
|
|
1832
|
-
function
|
|
2341
|
+
function reportToHeaderSection(report) {
|
|
1833
2342
|
const { packageName, version: version2 } = report;
|
|
1834
2343
|
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1835
2344
|
}
|
|
@@ -1869,16 +2378,16 @@ function logCategories({ categories, plugins }) {
|
|
|
1869
2378
|
applyScoreColor({ score }),
|
|
1870
2379
|
countCategoryAudits(refs, plugins)
|
|
1871
2380
|
]);
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
2381
|
+
const table5 = ui().table();
|
|
2382
|
+
table5.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
2383
|
+
table5.head(
|
|
1875
2384
|
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1876
2385
|
content: chalk4.cyan(heading),
|
|
1877
2386
|
hAlign: hAlign(idx)
|
|
1878
2387
|
}))
|
|
1879
2388
|
);
|
|
1880
2389
|
rows.forEach(
|
|
1881
|
-
(row) =>
|
|
2390
|
+
(row) => table5.row(
|
|
1882
2391
|
row.map((content, idx) => ({
|
|
1883
2392
|
content: content.toString(),
|
|
1884
2393
|
hAlign: hAlign(idx)
|
|
@@ -1887,19 +2396,19 @@ function logCategories({ categories, plugins }) {
|
|
|
1887
2396
|
);
|
|
1888
2397
|
log(chalk4.magentaBright.bold("Categories"));
|
|
1889
2398
|
log();
|
|
1890
|
-
|
|
2399
|
+
table5.render();
|
|
1891
2400
|
log();
|
|
1892
2401
|
}
|
|
1893
2402
|
function applyScoreColor({ score, text }) {
|
|
1894
2403
|
const formattedScore = text ?? formatReportScore(score);
|
|
1895
|
-
const
|
|
2404
|
+
const style = text ? chalk4 : chalk4.bold;
|
|
1896
2405
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1897
|
-
return
|
|
2406
|
+
return style.green(formattedScore);
|
|
1898
2407
|
}
|
|
1899
2408
|
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1900
|
-
return
|
|
2409
|
+
return style.yellow(formattedScore);
|
|
1901
2410
|
}
|
|
1902
|
-
return
|
|
2411
|
+
return style.red(formattedScore);
|
|
1903
2412
|
}
|
|
1904
2413
|
|
|
1905
2414
|
// packages/utils/src/lib/reports/scoring.ts
|
|
@@ -2061,12 +2570,22 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2061
2570
|
|
|
2062
2571
|
// packages/core/package.json
|
|
2063
2572
|
var name = "@code-pushup/core";
|
|
2064
|
-
var version = "0.
|
|
2573
|
+
var version = "0.42.0";
|
|
2065
2574
|
|
|
2066
2575
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2067
2576
|
import chalk5 from "chalk";
|
|
2068
2577
|
|
|
2069
2578
|
// packages/core/src/lib/normalize.ts
|
|
2579
|
+
function normalizeIssue(issue, gitRoot) {
|
|
2580
|
+
const { source, ...issueWithoutSource } = issue;
|
|
2581
|
+
return source == null ? issue : {
|
|
2582
|
+
...issueWithoutSource,
|
|
2583
|
+
source: {
|
|
2584
|
+
...source,
|
|
2585
|
+
file: formatGitPath(source.file, gitRoot)
|
|
2586
|
+
}
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2070
2589
|
async function normalizeAuditOutputs(audits) {
|
|
2071
2590
|
const gitRoot = await getGitRoot();
|
|
2072
2591
|
return audits.map((audit) => {
|
|
@@ -2078,13 +2597,7 @@ async function normalizeAuditOutputs(audits) {
|
|
|
2078
2597
|
details: {
|
|
2079
2598
|
...audit.details,
|
|
2080
2599
|
issues: audit.details.issues.map(
|
|
2081
|
-
(issue) => issue
|
|
2082
|
-
...issue,
|
|
2083
|
-
source: {
|
|
2084
|
-
...issue.source,
|
|
2085
|
-
file: formatGitPath(issue.source.file, gitRoot)
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2600
|
+
(issue) => normalizeIssue(issue, gitRoot)
|
|
2088
2601
|
)
|
|
2089
2602
|
}
|
|
2090
2603
|
};
|
|
@@ -2094,9 +2607,9 @@ async function normalizeAuditOutputs(audits) {
|
|
|
2094
2607
|
// packages/core/src/lib/implementation/runner.ts
|
|
2095
2608
|
import { join as join3 } from "node:path";
|
|
2096
2609
|
async function executeRunnerConfig(cfg, onProgress) {
|
|
2097
|
-
const { args, command, outputFile, outputTransform } = cfg;
|
|
2610
|
+
const { args, command: command2, outputFile, outputTransform } = cfg;
|
|
2098
2611
|
const { duration, date } = await executeProcess({
|
|
2099
|
-
command,
|
|
2612
|
+
command: command2,
|
|
2100
2613
|
args,
|
|
2101
2614
|
observer: { onStdout: onProgress }
|
|
2102
2615
|
});
|
|
@@ -2122,7 +2635,11 @@ async function executeRunnerFunction(runner, onProgress) {
|
|
|
2122
2635
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2123
2636
|
var PluginOutputMissingAuditError = class extends Error {
|
|
2124
2637
|
constructor(auditSlug) {
|
|
2125
|
-
super(
|
|
2638
|
+
super(
|
|
2639
|
+
`Audit metadata not present in plugin config. Missing slug: ${chalk5.bold(
|
|
2640
|
+
auditSlug
|
|
2641
|
+
)}`
|
|
2642
|
+
);
|
|
2126
2643
|
}
|
|
2127
2644
|
};
|
|
2128
2645
|
async function executePlugin(pluginConfig, onProgress) {
|
|
@@ -2136,7 +2653,11 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
2136
2653
|
} = pluginConfig;
|
|
2137
2654
|
const runnerResult = typeof runner === "object" ? await executeRunnerConfig(runner, onProgress) : await executeRunnerFunction(runner, onProgress);
|
|
2138
2655
|
const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult;
|
|
2139
|
-
const
|
|
2656
|
+
const result = auditOutputsSchema.safeParse(unvalidatedAuditOutputs);
|
|
2657
|
+
if (!result.success) {
|
|
2658
|
+
throw new Error(`Audit output is invalid: ${result.error.message}`);
|
|
2659
|
+
}
|
|
2660
|
+
const auditOutputs = result.data;
|
|
2140
2661
|
auditOutputsCorrelateWithPluginOutput(auditOutputs, pluginConfigAudits);
|
|
2141
2662
|
const normalizedAuditOutputs = await normalizeAuditOutputs(auditOutputs);
|
|
2142
2663
|
const auditReports = normalizedAuditOutputs.map(
|
|
@@ -2156,32 +2677,48 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
2156
2677
|
...groups2 && { groups: groups2 }
|
|
2157
2678
|
};
|
|
2158
2679
|
}
|
|
2680
|
+
var wrapProgress = async (pluginCfg, steps, progressBar) => {
|
|
2681
|
+
progressBar?.updateTitle(`Executing ${chalk5.bold(pluginCfg.title)}`);
|
|
2682
|
+
try {
|
|
2683
|
+
const pluginReport = await executePlugin(pluginCfg);
|
|
2684
|
+
progressBar?.incrementInSteps(steps);
|
|
2685
|
+
return pluginReport;
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
progressBar?.incrementInSteps(steps);
|
|
2688
|
+
throw new Error(
|
|
2689
|
+
error instanceof Error ? `- Plugin ${chalk5.bold(pluginCfg.title)} (${chalk5.bold(
|
|
2690
|
+
pluginCfg.slug
|
|
2691
|
+
)}) produced the following error:
|
|
2692
|
+
- ${error.message}` : String(error)
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2159
2696
|
async function executePlugins(plugins, options2) {
|
|
2160
2697
|
const { progress = false } = options2 ?? {};
|
|
2161
2698
|
const progressBar = progress ? getProgressBar("Run plugins") : null;
|
|
2162
|
-
const pluginsResult = await plugins.reduce(
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
progressBar?.incrementInSteps(plugins.length);
|
|
2170
|
-
return [
|
|
2171
|
-
...await acc,
|
|
2172
|
-
Promise.reject(error instanceof Error ? error.message : String(error))
|
|
2173
|
-
];
|
|
2174
|
-
}
|
|
2175
|
-
}, Promise.resolve([]));
|
|
2699
|
+
const pluginsResult = await plugins.reduce(
|
|
2700
|
+
async (acc, pluginCfg) => [
|
|
2701
|
+
...await acc,
|
|
2702
|
+
wrapProgress(pluginCfg, plugins.length, progressBar)
|
|
2703
|
+
],
|
|
2704
|
+
Promise.resolve([])
|
|
2705
|
+
);
|
|
2176
2706
|
progressBar?.endProgress("Done running plugins");
|
|
2177
2707
|
const errorsTransform = ({ reason }) => String(reason);
|
|
2178
2708
|
const results = await Promise.allSettled(pluginsResult);
|
|
2179
2709
|
logMultipleResults(results, "Plugins", void 0, errorsTransform);
|
|
2180
2710
|
const { fulfilled, rejected } = groupByStatus(results);
|
|
2181
2711
|
if (rejected.length > 0) {
|
|
2182
|
-
const errorMessages = rejected.map(({ reason }) => String(reason)).join("
|
|
2712
|
+
const errorMessages = rejected.map(({ reason }) => String(reason)).join("\n");
|
|
2183
2713
|
throw new Error(
|
|
2184
|
-
`
|
|
2714
|
+
`Executing ${pluralizeToken(
|
|
2715
|
+
"plugin",
|
|
2716
|
+
rejected.length
|
|
2717
|
+
)} failed.
|
|
2718
|
+
|
|
2719
|
+
${errorMessages}
|
|
2720
|
+
|
|
2721
|
+
`
|
|
2185
2722
|
);
|
|
2186
2723
|
}
|
|
2187
2724
|
return fulfilled.map((result) => result.value);
|
|
@@ -2344,8 +2881,7 @@ function compareAudits2(reports) {
|
|
|
2344
2881
|
}
|
|
2345
2882
|
function categoryToResult(category) {
|
|
2346
2883
|
return {
|
|
2347
|
-
|
|
2348
|
-
title: category.title,
|
|
2884
|
+
...selectMeta(category),
|
|
2349
2885
|
score: category.score
|
|
2350
2886
|
};
|
|
2351
2887
|
}
|
|
@@ -2354,8 +2890,7 @@ function categoryPairToDiff({
|
|
|
2354
2890
|
after
|
|
2355
2891
|
}) {
|
|
2356
2892
|
return {
|
|
2357
|
-
|
|
2358
|
-
title: after.title,
|
|
2893
|
+
...selectMeta(after),
|
|
2359
2894
|
scores: {
|
|
2360
2895
|
before: before.score,
|
|
2361
2896
|
after: after.score,
|
|
@@ -2365,12 +2900,8 @@ function categoryPairToDiff({
|
|
|
2365
2900
|
}
|
|
2366
2901
|
function pluginGroupToResult({ group, plugin }) {
|
|
2367
2902
|
return {
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
plugin: {
|
|
2371
|
-
slug: plugin.slug,
|
|
2372
|
-
title: plugin.title
|
|
2373
|
-
},
|
|
2903
|
+
...selectMeta(group),
|
|
2904
|
+
plugin: selectMeta(plugin),
|
|
2374
2905
|
score: group.score
|
|
2375
2906
|
};
|
|
2376
2907
|
}
|
|
@@ -2379,12 +2910,8 @@ function pluginGroupPairToDiff({
|
|
|
2379
2910
|
after
|
|
2380
2911
|
}) {
|
|
2381
2912
|
return {
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
plugin: {
|
|
2385
|
-
slug: after.plugin.slug,
|
|
2386
|
-
title: after.plugin.title
|
|
2387
|
-
},
|
|
2913
|
+
...selectMeta(after.group),
|
|
2914
|
+
plugin: selectMeta(after.plugin),
|
|
2388
2915
|
scores: {
|
|
2389
2916
|
before: before.group.score,
|
|
2390
2917
|
after: after.group.score,
|
|
@@ -2394,12 +2921,8 @@ function pluginGroupPairToDiff({
|
|
|
2394
2921
|
}
|
|
2395
2922
|
function pluginAuditToResult({ audit, plugin }) {
|
|
2396
2923
|
return {
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
plugin: {
|
|
2400
|
-
slug: plugin.slug,
|
|
2401
|
-
title: plugin.title
|
|
2402
|
-
},
|
|
2924
|
+
...selectMeta(audit),
|
|
2925
|
+
plugin: selectMeta(plugin),
|
|
2403
2926
|
score: audit.score,
|
|
2404
2927
|
value: audit.value,
|
|
2405
2928
|
displayValue: audit.displayValue
|
|
@@ -2410,12 +2933,8 @@ function pluginAuditPairToDiff({
|
|
|
2410
2933
|
after
|
|
2411
2934
|
}) {
|
|
2412
2935
|
return {
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
plugin: {
|
|
2416
|
-
slug: after.plugin.slug,
|
|
2417
|
-
title: after.plugin.title
|
|
2418
|
-
},
|
|
2936
|
+
...selectMeta(after.audit),
|
|
2937
|
+
plugin: selectMeta(after.plugin),
|
|
2419
2938
|
scores: {
|
|
2420
2939
|
before: before.audit.score,
|
|
2421
2940
|
after: after.audit.score,
|
|
@@ -2432,6 +2951,15 @@ function pluginAuditPairToDiff({
|
|
|
2432
2951
|
}
|
|
2433
2952
|
};
|
|
2434
2953
|
}
|
|
2954
|
+
function selectMeta(meta) {
|
|
2955
|
+
return {
|
|
2956
|
+
slug: meta.slug,
|
|
2957
|
+
title: meta.title,
|
|
2958
|
+
...meta.docsUrl && {
|
|
2959
|
+
docsUrl: meta.docsUrl
|
|
2960
|
+
}
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2435
2963
|
|
|
2436
2964
|
// packages/core/src/lib/compare.ts
|
|
2437
2965
|
async function compareReportFiles(inputPaths, persistConfig) {
|
|
@@ -2487,46 +3015,6 @@ function reportsDiffToFileContent(reportsDiff, format) {
|
|
|
2487
3015
|
}
|
|
2488
3016
|
}
|
|
2489
3017
|
|
|
2490
|
-
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2491
|
-
import { join as join6 } from "node:path";
|
|
2492
|
-
var ConfigPathError = class extends Error {
|
|
2493
|
-
constructor(configPath) {
|
|
2494
|
-
super(`Provided path '${configPath}' is not valid.`);
|
|
2495
|
-
}
|
|
2496
|
-
};
|
|
2497
|
-
async function readRcByPath(filepath, tsconfig) {
|
|
2498
|
-
if (filepath.length === 0) {
|
|
2499
|
-
throw new Error("The path to the configuration file is empty.");
|
|
2500
|
-
}
|
|
2501
|
-
if (!await fileExists(filepath)) {
|
|
2502
|
-
throw new ConfigPathError(filepath);
|
|
2503
|
-
}
|
|
2504
|
-
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
2505
|
-
return coreConfigSchema.parse(cfg);
|
|
2506
|
-
}
|
|
2507
|
-
async function autoloadRc(tsconfig) {
|
|
2508
|
-
let ext = "";
|
|
2509
|
-
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
2510
|
-
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
2511
|
-
const exists2 = await fileExists(path);
|
|
2512
|
-
if (exists2) {
|
|
2513
|
-
ext = extension;
|
|
2514
|
-
break;
|
|
2515
|
-
}
|
|
2516
|
-
}
|
|
2517
|
-
if (!ext) {
|
|
2518
|
-
throw new Error(
|
|
2519
|
-
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
2520
|
-
"|"
|
|
2521
|
-
)}) present in ${process.cwd()}`
|
|
2522
|
-
);
|
|
2523
|
-
}
|
|
2524
|
-
return readRcByPath(
|
|
2525
|
-
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2526
|
-
tsconfig
|
|
2527
|
-
);
|
|
2528
|
-
}
|
|
2529
|
-
|
|
2530
3018
|
// packages/core/src/lib/upload.ts
|
|
2531
3019
|
import {
|
|
2532
3020
|
uploadToPortal
|
|
@@ -2572,17 +3060,33 @@ function groupToGQL(group) {
|
|
|
2572
3060
|
};
|
|
2573
3061
|
}
|
|
2574
3062
|
function auditToGQL(audit) {
|
|
3063
|
+
const {
|
|
3064
|
+
slug,
|
|
3065
|
+
title,
|
|
3066
|
+
description,
|
|
3067
|
+
docsUrl,
|
|
3068
|
+
score,
|
|
3069
|
+
value,
|
|
3070
|
+
displayValue: formattedValue,
|
|
3071
|
+
details: details4
|
|
3072
|
+
} = audit;
|
|
3073
|
+
const {
|
|
3074
|
+
issues
|
|
3075
|
+
/*, table */
|
|
3076
|
+
} = details4 ?? {};
|
|
2575
3077
|
return {
|
|
2576
|
-
slug
|
|
2577
|
-
title
|
|
2578
|
-
description
|
|
2579
|
-
docsUrl
|
|
2580
|
-
score
|
|
2581
|
-
value
|
|
2582
|
-
formattedValue
|
|
2583
|
-
...
|
|
3078
|
+
slug,
|
|
3079
|
+
title,
|
|
3080
|
+
description,
|
|
3081
|
+
docsUrl,
|
|
3082
|
+
score,
|
|
3083
|
+
value,
|
|
3084
|
+
formattedValue,
|
|
3085
|
+
...details4 && {
|
|
2584
3086
|
details: {
|
|
2585
|
-
issues:
|
|
3087
|
+
...issues && { issues: issues.map(issueToGQL) }
|
|
3088
|
+
// @TODO add when https://github.com/code-pushup/cli/issues/530 is implemented
|
|
3089
|
+
// ...(table ? {table} : {}),
|
|
2586
3090
|
}
|
|
2587
3091
|
}
|
|
2588
3092
|
};
|
|
@@ -2606,6 +3110,7 @@ function categoryToGQL(category) {
|
|
|
2606
3110
|
slug: category.slug,
|
|
2607
3111
|
title: category.title,
|
|
2608
3112
|
description: category.description,
|
|
3113
|
+
isBinary: category.isBinary,
|
|
2609
3114
|
refs: category.refs.map((ref) => ({
|
|
2610
3115
|
plugin: ref.plugin,
|
|
2611
3116
|
type: categoryRefTypeToGQL(ref.type),
|
|
@@ -2655,6 +3160,80 @@ async function upload(options2, uploadFn = uploadToPortal) {
|
|
|
2655
3160
|
return uploadFn({ apiKey, server, data, timeout });
|
|
2656
3161
|
}
|
|
2657
3162
|
|
|
3163
|
+
// packages/core/src/lib/history.ts
|
|
3164
|
+
async function history(config, commits) {
|
|
3165
|
+
const initialBranch = await getCurrentBranchOrTag();
|
|
3166
|
+
const { skipUploads = false, forceCleanStatus, persist } = config;
|
|
3167
|
+
const reports = [];
|
|
3168
|
+
for (const commit of commits) {
|
|
3169
|
+
ui().logger.info(`Collect ${commit}`);
|
|
3170
|
+
await safeCheckout(commit, forceCleanStatus);
|
|
3171
|
+
const currentConfig = {
|
|
3172
|
+
...config,
|
|
3173
|
+
persist: {
|
|
3174
|
+
...persist,
|
|
3175
|
+
format: ["json"],
|
|
3176
|
+
filename: `${commit}-report`
|
|
3177
|
+
}
|
|
3178
|
+
};
|
|
3179
|
+
await collectAndPersistReports(currentConfig);
|
|
3180
|
+
if (skipUploads) {
|
|
3181
|
+
ui().logger.info("Upload is skipped because skipUploads is set to true.");
|
|
3182
|
+
} else {
|
|
3183
|
+
if (currentConfig.upload) {
|
|
3184
|
+
await upload(currentConfig);
|
|
3185
|
+
} else {
|
|
3186
|
+
ui().logger.info(
|
|
3187
|
+
"Upload is skipped because upload config is undefined."
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
reports.push(currentConfig.persist.filename);
|
|
3192
|
+
}
|
|
3193
|
+
await safeCheckout(initialBranch, forceCleanStatus);
|
|
3194
|
+
return reports;
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
3198
|
+
import { join as join6 } from "node:path";
|
|
3199
|
+
var ConfigPathError = class extends Error {
|
|
3200
|
+
constructor(configPath) {
|
|
3201
|
+
super(`Provided path '${configPath}' is not valid.`);
|
|
3202
|
+
}
|
|
3203
|
+
};
|
|
3204
|
+
async function readRcByPath(filepath, tsconfig) {
|
|
3205
|
+
if (filepath.length === 0) {
|
|
3206
|
+
throw new Error("The path to the configuration file is empty.");
|
|
3207
|
+
}
|
|
3208
|
+
if (!await fileExists(filepath)) {
|
|
3209
|
+
throw new ConfigPathError(filepath);
|
|
3210
|
+
}
|
|
3211
|
+
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
3212
|
+
return coreConfigSchema.parse(cfg);
|
|
3213
|
+
}
|
|
3214
|
+
async function autoloadRc(tsconfig) {
|
|
3215
|
+
let ext = "";
|
|
3216
|
+
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
3217
|
+
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
3218
|
+
const exists2 = await fileExists(path);
|
|
3219
|
+
if (exists2) {
|
|
3220
|
+
ext = extension;
|
|
3221
|
+
break;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
if (!ext) {
|
|
3225
|
+
throw new Error(
|
|
3226
|
+
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
3227
|
+
"|"
|
|
3228
|
+
)}) present in ${process.cwd()}`
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
return readRcByPath(
|
|
3232
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
3233
|
+
tsconfig
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
|
|
2658
3237
|
// packages/cli/src/lib/constants.ts
|
|
2659
3238
|
var CLI_NAME = "Code PushUp CLI";
|
|
2660
3239
|
var CLI_SCRIPT_NAME = "code-pushup";
|
|
@@ -2664,7 +3243,7 @@ import chalk6 from "chalk";
|
|
|
2664
3243
|
function renderConfigureCategoriesHint() {
|
|
2665
3244
|
ui().logger.info(
|
|
2666
3245
|
chalk6.gray(
|
|
2667
|
-
`\u{1F4A1} Configure categories to see the scores in an overview table. See: ${
|
|
3246
|
+
`\u{1F4A1} Configure categories to see the scores in an overview table. See: ${link3(
|
|
2668
3247
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md"
|
|
2669
3248
|
)}`
|
|
2670
3249
|
)
|
|
@@ -2672,7 +3251,7 @@ function renderConfigureCategoriesHint() {
|
|
|
2672
3251
|
}
|
|
2673
3252
|
function uploadSuccessfulLog(url) {
|
|
2674
3253
|
ui().logger.success("Upload successful!");
|
|
2675
|
-
ui().logger.success(
|
|
3254
|
+
ui().logger.success(link3(url));
|
|
2676
3255
|
}
|
|
2677
3256
|
function collectSuccessfulLog() {
|
|
2678
3257
|
ui().logger.success("Collecting report successful!");
|
|
@@ -2683,15 +3262,15 @@ function renderIntegratePortalHint() {
|
|
|
2683
3262
|
"npx code-pushup upload"
|
|
2684
3263
|
)}`
|
|
2685
3264
|
).add(
|
|
2686
|
-
` ${
|
|
3265
|
+
` ${link3(
|
|
2687
3266
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#upload-command"
|
|
2688
3267
|
)}`
|
|
2689
3268
|
).add(
|
|
2690
|
-
`${chalk6.gray("\u276F")} ${chalk6.gray("Portal Integration")} - ${
|
|
3269
|
+
`${chalk6.gray("\u276F")} ${chalk6.gray("Portal Integration")} - ${link3(
|
|
2691
3270
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#portal-integration"
|
|
2692
3271
|
)}`
|
|
2693
3272
|
).add(
|
|
2694
|
-
`${chalk6.gray("\u276F")} ${chalk6.gray("Upload Command")} - ${
|
|
3273
|
+
`${chalk6.gray("\u276F")} ${chalk6.gray("Upload Command")} - ${link3(
|
|
2695
3274
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#portal-integration"
|
|
2696
3275
|
)}`
|
|
2697
3276
|
).render();
|
|
@@ -2699,13 +3278,13 @@ function renderIntegratePortalHint() {
|
|
|
2699
3278
|
|
|
2700
3279
|
// packages/cli/src/lib/autorun/autorun-command.ts
|
|
2701
3280
|
function yargsAutorunCommandObject() {
|
|
2702
|
-
const
|
|
3281
|
+
const command2 = "autorun";
|
|
2703
3282
|
return {
|
|
2704
|
-
command,
|
|
3283
|
+
command: command2,
|
|
2705
3284
|
describe: "Shortcut for running collect followed by upload",
|
|
2706
3285
|
handler: async (args) => {
|
|
2707
3286
|
ui().logger.log(chalk7.bold(CLI_NAME));
|
|
2708
|
-
ui().logger.info(chalk7.gray(`Run ${
|
|
3287
|
+
ui().logger.info(chalk7.gray(`Run ${command2}...`));
|
|
2709
3288
|
const options2 = args;
|
|
2710
3289
|
const optionsWithFormat = {
|
|
2711
3290
|
...options2,
|
|
@@ -2735,14 +3314,14 @@ function yargsAutorunCommandObject() {
|
|
|
2735
3314
|
// packages/cli/src/lib/collect/collect-command.ts
|
|
2736
3315
|
import chalk8 from "chalk";
|
|
2737
3316
|
function yargsCollectCommandObject() {
|
|
2738
|
-
const
|
|
3317
|
+
const command2 = "collect";
|
|
2739
3318
|
return {
|
|
2740
|
-
command,
|
|
3319
|
+
command: command2,
|
|
2741
3320
|
describe: "Run Plugins and collect results",
|
|
2742
3321
|
handler: async (args) => {
|
|
2743
3322
|
const options2 = args;
|
|
2744
3323
|
ui().logger.log(chalk8.bold(CLI_NAME));
|
|
2745
|
-
ui().logger.info(chalk8.gray(`Run ${
|
|
3324
|
+
ui().logger.info(chalk8.gray(`Run ${command2}...`));
|
|
2746
3325
|
await collectAndPersistReports(options2);
|
|
2747
3326
|
collectSuccessfulLog();
|
|
2748
3327
|
if (options2.categories.length === 0) {
|
|
@@ -2761,7 +3340,7 @@ function renderUploadAutorunHint() {
|
|
|
2761
3340
|
"Run upload to upload the created report to the server"
|
|
2762
3341
|
)}`
|
|
2763
3342
|
).add(
|
|
2764
|
-
` ${
|
|
3343
|
+
` ${link3(
|
|
2765
3344
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#upload-command"
|
|
2766
3345
|
)}`
|
|
2767
3346
|
).add(
|
|
@@ -2769,7 +3348,7 @@ function renderUploadAutorunHint() {
|
|
|
2769
3348
|
"Run collect & upload"
|
|
2770
3349
|
)}`
|
|
2771
3350
|
).add(
|
|
2772
|
-
` ${
|
|
3351
|
+
` ${link3(
|
|
2773
3352
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#autorun-command"
|
|
2774
3353
|
)}`
|
|
2775
3354
|
).render();
|
|
@@ -2796,14 +3375,14 @@ function yargsCompareOptionsDefinition() {
|
|
|
2796
3375
|
|
|
2797
3376
|
// packages/cli/src/lib/compare/compare-command.ts
|
|
2798
3377
|
function yargsCompareCommandObject() {
|
|
2799
|
-
const
|
|
3378
|
+
const command2 = "compare";
|
|
2800
3379
|
return {
|
|
2801
|
-
command,
|
|
3380
|
+
command: command2,
|
|
2802
3381
|
describe: "Compare 2 report files and create a diff file",
|
|
2803
3382
|
builder: yargsCompareOptionsDefinition(),
|
|
2804
3383
|
handler: async (args) => {
|
|
2805
3384
|
ui().logger.log(chalk9.bold(CLI_NAME));
|
|
2806
|
-
ui().logger.info(chalk9.gray(`Run ${
|
|
3385
|
+
ui().logger.info(chalk9.gray(`Run ${command2}...`));
|
|
2807
3386
|
const options2 = args;
|
|
2808
3387
|
const { before, after, persist } = options2;
|
|
2809
3388
|
const outputPaths = await compareReportFiles({ before, after }, persist);
|
|
@@ -2814,6 +3393,9 @@ function yargsCompareCommandObject() {
|
|
|
2814
3393
|
};
|
|
2815
3394
|
}
|
|
2816
3395
|
|
|
3396
|
+
// packages/cli/src/lib/history/history-command.ts
|
|
3397
|
+
import chalk10 from "chalk";
|
|
3398
|
+
|
|
2817
3399
|
// packages/cli/src/lib/implementation/global.utils.ts
|
|
2818
3400
|
function filterKebabCaseKeys(obj) {
|
|
2819
3401
|
return Object.entries(obj).filter(([key]) => !key.includes("-")).reduce(
|
|
@@ -2839,11 +3421,146 @@ function coerceArray(param) {
|
|
|
2839
3421
|
return [...new Set(toArray(param).flatMap((f) => f.split(",")))];
|
|
2840
3422
|
}
|
|
2841
3423
|
|
|
3424
|
+
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
3425
|
+
var onlyPluginsOption = {
|
|
3426
|
+
describe: "List of plugins to run. If not set all plugins are run.",
|
|
3427
|
+
type: "array",
|
|
3428
|
+
default: [],
|
|
3429
|
+
coerce: coerceArray
|
|
3430
|
+
};
|
|
3431
|
+
function yargsOnlyPluginsOptionsDefinition() {
|
|
3432
|
+
return {
|
|
3433
|
+
onlyPlugins: onlyPluginsOption
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
// packages/cli/src/lib/history/history.options.ts
|
|
3438
|
+
function yargsHistoryOptionsDefinition() {
|
|
3439
|
+
return {
|
|
3440
|
+
targetBranch: {
|
|
3441
|
+
describe: "Branch to crawl history",
|
|
3442
|
+
type: "string"
|
|
3443
|
+
},
|
|
3444
|
+
onlySemverTags: {
|
|
3445
|
+
describe: "Skip commits not tagged with a semantic version",
|
|
3446
|
+
type: "boolean",
|
|
3447
|
+
default: false
|
|
3448
|
+
},
|
|
3449
|
+
forceCleanStatus: {
|
|
3450
|
+
describe: "If we reset the status to a clean git history forcefully or not.",
|
|
3451
|
+
type: "boolean",
|
|
3452
|
+
default: false
|
|
3453
|
+
},
|
|
3454
|
+
skipUploads: {
|
|
3455
|
+
describe: "Upload created reports",
|
|
3456
|
+
type: "boolean",
|
|
3457
|
+
default: false
|
|
3458
|
+
},
|
|
3459
|
+
maxCount: {
|
|
3460
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt---max-countltnumbergt
|
|
3461
|
+
describe: "Number of steps in history",
|
|
3462
|
+
type: "number",
|
|
3463
|
+
// eslint-disable-next-line no-magic-numbers
|
|
3464
|
+
default: 5
|
|
3465
|
+
},
|
|
3466
|
+
from: {
|
|
3467
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
|
|
3468
|
+
describe: "hash to first commit in history",
|
|
3469
|
+
type: "string"
|
|
3470
|
+
},
|
|
3471
|
+
to: {
|
|
3472
|
+
// https://git-scm.com/docs/git-log#Documentation/git-log.txt-ltrevision-rangegt
|
|
3473
|
+
describe: "hash to last commit in history",
|
|
3474
|
+
type: "string"
|
|
3475
|
+
}
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
// packages/cli/src/lib/history/utils.ts
|
|
3480
|
+
async function normalizeHashOptions(processArgs) {
|
|
3481
|
+
const {
|
|
3482
|
+
onlySemverTags,
|
|
3483
|
+
// overwritten
|
|
3484
|
+
maxCount,
|
|
3485
|
+
...opt
|
|
3486
|
+
} = processArgs;
|
|
3487
|
+
let { from, to, ...processOptions } = opt;
|
|
3488
|
+
if (!onlySemverTags) {
|
|
3489
|
+
if (from && isSemver(from)) {
|
|
3490
|
+
const { hash } = await getHashFromTag(from);
|
|
3491
|
+
from = hash;
|
|
3492
|
+
}
|
|
3493
|
+
if (to && isSemver(to)) {
|
|
3494
|
+
const { hash } = await getHashFromTag(to);
|
|
3495
|
+
to = hash;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
return {
|
|
3499
|
+
...processOptions,
|
|
3500
|
+
onlySemverTags,
|
|
3501
|
+
maxCount: maxCount && maxCount > 0 ? maxCount : void 0,
|
|
3502
|
+
from,
|
|
3503
|
+
to
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// packages/cli/src/lib/history/history-command.ts
|
|
3508
|
+
var command = "history";
|
|
3509
|
+
async function handler(args) {
|
|
3510
|
+
ui().logger.info(chalk10.bold(CLI_NAME));
|
|
3511
|
+
ui().logger.info(chalk10.gray(`Run ${command}`));
|
|
3512
|
+
const currentBranch = await getCurrentBranchOrTag();
|
|
3513
|
+
const { targetBranch: rawTargetBranch, ...opt } = args;
|
|
3514
|
+
const {
|
|
3515
|
+
targetBranch,
|
|
3516
|
+
from,
|
|
3517
|
+
to,
|
|
3518
|
+
maxCount,
|
|
3519
|
+
onlySemverTags,
|
|
3520
|
+
...historyOptions
|
|
3521
|
+
} = await normalizeHashOptions({
|
|
3522
|
+
...opt,
|
|
3523
|
+
targetBranch: rawTargetBranch ?? currentBranch
|
|
3524
|
+
});
|
|
3525
|
+
const filterOptions = { targetBranch, from, to, maxCount };
|
|
3526
|
+
const results = onlySemverTags ? await getSemverTags(filterOptions) : await getHashes(filterOptions);
|
|
3527
|
+
try {
|
|
3528
|
+
const reports = await history(
|
|
3529
|
+
{
|
|
3530
|
+
targetBranch,
|
|
3531
|
+
...historyOptions
|
|
3532
|
+
},
|
|
3533
|
+
results.map(({ hash }) => hash)
|
|
3534
|
+
);
|
|
3535
|
+
ui().logger.log(`Reports: ${reports.length}`);
|
|
3536
|
+
} finally {
|
|
3537
|
+
await safeCheckout(currentBranch);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
function yargsHistoryCommandObject() {
|
|
3541
|
+
return {
|
|
3542
|
+
command,
|
|
3543
|
+
describe: "Collect reports for commit history",
|
|
3544
|
+
builder: (yargs2) => {
|
|
3545
|
+
yargs2.options({
|
|
3546
|
+
...yargsHistoryOptionsDefinition(),
|
|
3547
|
+
...yargsOnlyPluginsOptionsDefinition()
|
|
3548
|
+
});
|
|
3549
|
+
yargs2.group(
|
|
3550
|
+
Object.keys(yargsHistoryOptionsDefinition()),
|
|
3551
|
+
"History Options:"
|
|
3552
|
+
);
|
|
3553
|
+
return yargs2;
|
|
3554
|
+
},
|
|
3555
|
+
handler
|
|
3556
|
+
};
|
|
3557
|
+
}
|
|
3558
|
+
|
|
2842
3559
|
// packages/cli/src/lib/print-config/print-config-command.ts
|
|
2843
3560
|
function yargsConfigCommandObject() {
|
|
2844
|
-
const
|
|
3561
|
+
const command2 = "print-config";
|
|
2845
3562
|
return {
|
|
2846
|
-
command,
|
|
3563
|
+
command: command2,
|
|
2847
3564
|
describe: "Print config",
|
|
2848
3565
|
handler: (yargsArgs) => {
|
|
2849
3566
|
const { _, $0, ...args } = yargsArgs;
|
|
@@ -2854,15 +3571,15 @@ function yargsConfigCommandObject() {
|
|
|
2854
3571
|
}
|
|
2855
3572
|
|
|
2856
3573
|
// packages/cli/src/lib/upload/upload-command.ts
|
|
2857
|
-
import
|
|
3574
|
+
import chalk11 from "chalk";
|
|
2858
3575
|
function yargsUploadCommandObject() {
|
|
2859
|
-
const
|
|
3576
|
+
const command2 = "upload";
|
|
2860
3577
|
return {
|
|
2861
|
-
command,
|
|
3578
|
+
command: command2,
|
|
2862
3579
|
describe: "Upload report results to the portal",
|
|
2863
3580
|
handler: async (args) => {
|
|
2864
|
-
ui().logger.log(
|
|
2865
|
-
ui().logger.info(
|
|
3581
|
+
ui().logger.log(chalk11.bold(CLI_NAME));
|
|
3582
|
+
ui().logger.info(chalk11.gray(`Run ${command2}...`));
|
|
2866
3583
|
const options2 = args;
|
|
2867
3584
|
if (options2.upload == null) {
|
|
2868
3585
|
renderIntegratePortalHint();
|
|
@@ -2883,6 +3600,7 @@ var commands = [
|
|
|
2883
3600
|
yargsAutorunCommandObject(),
|
|
2884
3601
|
yargsCollectCommandObject(),
|
|
2885
3602
|
yargsUploadCommandObject(),
|
|
3603
|
+
yargsHistoryCommandObject(),
|
|
2886
3604
|
yargsCompareCommandObject(),
|
|
2887
3605
|
yargsConfigCommandObject()
|
|
2888
3606
|
];
|
|
@@ -2922,7 +3640,7 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
2922
3640
|
}
|
|
2923
3641
|
|
|
2924
3642
|
// packages/cli/src/lib/implementation/only-plugins.utils.ts
|
|
2925
|
-
import
|
|
3643
|
+
import chalk12 from "chalk";
|
|
2926
3644
|
function validateOnlyPluginsOption({
|
|
2927
3645
|
plugins,
|
|
2928
3646
|
categories
|
|
@@ -2936,7 +3654,7 @@ function validateOnlyPluginsOption({
|
|
|
2936
3654
|
);
|
|
2937
3655
|
if (missingPlugins.length > 0 && verbose) {
|
|
2938
3656
|
ui().logger.info(
|
|
2939
|
-
`${
|
|
3657
|
+
`${chalk12.yellow(
|
|
2940
3658
|
"\u26A0"
|
|
2941
3659
|
)} The --onlyPlugin argument references plugins with "${missingPlugins.join(
|
|
2942
3660
|
'", "'
|
|
@@ -3063,19 +3781,6 @@ function yargsGlobalOptionsDefinition() {
|
|
|
3063
3781
|
};
|
|
3064
3782
|
}
|
|
3065
3783
|
|
|
3066
|
-
// packages/cli/src/lib/implementation/only-plugins.options.ts
|
|
3067
|
-
var onlyPluginsOption = {
|
|
3068
|
-
describe: "List of plugins to run. If not set all plugins are run.",
|
|
3069
|
-
type: "array",
|
|
3070
|
-
default: [],
|
|
3071
|
-
coerce: coerceArray
|
|
3072
|
-
};
|
|
3073
|
-
function yargsOnlyPluginsOptionsDefinition() {
|
|
3074
|
-
return {
|
|
3075
|
-
onlyPlugins: onlyPluginsOption
|
|
3076
|
-
};
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
3784
|
// packages/cli/src/lib/options.ts
|
|
3080
3785
|
var options = {
|
|
3081
3786
|
...yargsGlobalOptionsDefinition(),
|
|
@@ -3092,7 +3797,7 @@ var groups = {
|
|
|
3092
3797
|
};
|
|
3093
3798
|
|
|
3094
3799
|
// packages/cli/src/lib/yargs-cli.ts
|
|
3095
|
-
import
|
|
3800
|
+
import chalk13 from "chalk";
|
|
3096
3801
|
import yargs from "yargs";
|
|
3097
3802
|
function yargsCli(argv, cfg) {
|
|
3098
3803
|
const { usageMessage, scriptName, noExitProcess } = cfg;
|
|
@@ -3112,7 +3817,7 @@ function yargsCli(argv, cfg) {
|
|
|
3112
3817
|
(config) => Array.isArray(config) ? config.at(-1) : config
|
|
3113
3818
|
).options(options2).wrap(TERMINAL_WIDTH);
|
|
3114
3819
|
if (usageMessage) {
|
|
3115
|
-
cli2.usage(
|
|
3820
|
+
cli2.usage(chalk13.bold(usageMessage));
|
|
3116
3821
|
}
|
|
3117
3822
|
if (scriptName) {
|
|
3118
3823
|
cli2.scriptName(scriptName);
|