@code-pushup/core 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 +1076 -586
- package/package.json +5 -6
- package/src/index.d.ts +1 -0
- package/src/lib/history.d.ts +1 -4
- package/src/lib/normalize.d.ts +2 -1
package/index.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// packages/models/src/lib/audit.ts
|
|
2
|
-
import { z as z2 } from "zod";
|
|
3
|
-
|
|
4
1
|
// packages/models/src/lib/implementation/schemas.ts
|
|
5
2
|
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
6
3
|
import { z } from "zod";
|
|
@@ -66,6 +63,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
|
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
// packages/models/src/lib/implementation/schemas.ts
|
|
66
|
+
var primitiveValueSchema = z.union([z.string(), z.number()]);
|
|
69
67
|
function executionMetaSchema(options = {
|
|
70
68
|
descriptionDate: "Execution start date and time",
|
|
71
69
|
descriptionDuration: "Execution duration in ms"
|
|
@@ -157,6 +155,7 @@ function hasNonZeroWeightedRef(refs) {
|
|
|
157
155
|
}
|
|
158
156
|
|
|
159
157
|
// packages/models/src/lib/audit.ts
|
|
158
|
+
import { z as z2 } from "zod";
|
|
160
159
|
var auditSchema = z2.object({
|
|
161
160
|
slug: slugSchema.describe("ID (unique within plugin)")
|
|
162
161
|
}).merge(
|
|
@@ -186,7 +185,7 @@ function getDuplicateSlugsInAudits(audits) {
|
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
// packages/models/src/lib/audit-output.ts
|
|
189
|
-
import { z as
|
|
188
|
+
import { z as z5 } from "zod";
|
|
190
189
|
|
|
191
190
|
// packages/models/src/lib/issue.ts
|
|
192
191
|
import { z as z3 } from "zod";
|
|
@@ -217,16 +216,61 @@ var issueSchema = z3.object(
|
|
|
217
216
|
{ description: "Issue information" }
|
|
218
217
|
);
|
|
219
218
|
|
|
219
|
+
// packages/models/src/lib/table.ts
|
|
220
|
+
import { z as z4 } from "zod";
|
|
221
|
+
var tableAlignmentSchema = z4.enum(["left", "center", "right"], {
|
|
222
|
+
description: "Cell alignment"
|
|
223
|
+
});
|
|
224
|
+
var tableColumnObjectSchema = z4.object({
|
|
225
|
+
key: z4.string(),
|
|
226
|
+
label: z4.string().optional(),
|
|
227
|
+
align: tableAlignmentSchema.optional()
|
|
228
|
+
});
|
|
229
|
+
var tableRowObjectSchema = z4.record(primitiveValueSchema, {
|
|
230
|
+
description: "Object row"
|
|
231
|
+
});
|
|
232
|
+
var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
|
|
233
|
+
description: "Primitive row"
|
|
234
|
+
});
|
|
235
|
+
var tableSharedSchema = z4.object({
|
|
236
|
+
title: z4.string().optional().describe("Display title for table")
|
|
237
|
+
});
|
|
238
|
+
var tablePrimitiveSchema = tableSharedSchema.merge(
|
|
239
|
+
z4.object(
|
|
240
|
+
{
|
|
241
|
+
columns: z4.array(tableAlignmentSchema).optional(),
|
|
242
|
+
rows: z4.array(tableRowPrimitiveSchema)
|
|
243
|
+
},
|
|
244
|
+
{ description: "Table with primitive rows and optional alignment columns" }
|
|
245
|
+
)
|
|
246
|
+
);
|
|
247
|
+
var tableObjectSchema = tableSharedSchema.merge(
|
|
248
|
+
z4.object(
|
|
249
|
+
{
|
|
250
|
+
columns: z4.union([
|
|
251
|
+
z4.array(tableAlignmentSchema),
|
|
252
|
+
z4.array(tableColumnObjectSchema)
|
|
253
|
+
]).optional(),
|
|
254
|
+
rows: z4.array(tableRowObjectSchema)
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
description: "Table with object rows and optional alignment or object columns"
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
);
|
|
261
|
+
var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
|
|
262
|
+
|
|
220
263
|
// packages/models/src/lib/audit-output.ts
|
|
221
264
|
var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
|
|
222
|
-
var auditDisplayValueSchema =
|
|
223
|
-
var auditDetailsSchema =
|
|
265
|
+
var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
266
|
+
var auditDetailsSchema = z5.object(
|
|
224
267
|
{
|
|
225
|
-
issues:
|
|
268
|
+
issues: z5.array(issueSchema, { description: "List of findings" }).optional(),
|
|
269
|
+
table: tableSchema("Table of related findings").optional()
|
|
226
270
|
},
|
|
227
271
|
{ description: "Detailed information" }
|
|
228
272
|
);
|
|
229
|
-
var auditOutputSchema =
|
|
273
|
+
var auditOutputSchema = z5.object(
|
|
230
274
|
{
|
|
231
275
|
slug: slugSchema.describe("Reference to audit"),
|
|
232
276
|
displayValue: auditDisplayValueSchema,
|
|
@@ -236,7 +280,7 @@ var auditOutputSchema = z4.object(
|
|
|
236
280
|
},
|
|
237
281
|
{ description: "Audit information" }
|
|
238
282
|
);
|
|
239
|
-
var auditOutputsSchema =
|
|
283
|
+
var auditOutputsSchema = z5.array(auditOutputSchema, {
|
|
240
284
|
description: "List of JSON formatted audit output emitted by the runner process of a plugin"
|
|
241
285
|
}).refine(
|
|
242
286
|
(audits) => !getDuplicateSlugsInAudits2(audits),
|
|
@@ -253,13 +297,13 @@ function getDuplicateSlugsInAudits2(audits) {
|
|
|
253
297
|
}
|
|
254
298
|
|
|
255
299
|
// packages/models/src/lib/category-config.ts
|
|
256
|
-
import { z as
|
|
300
|
+
import { z as z6 } from "zod";
|
|
257
301
|
var categoryRefSchema = weightedRefSchema(
|
|
258
302
|
"Weighted references to audits and/or groups for the category",
|
|
259
303
|
"Slug of an audit or group (depending on `type`)"
|
|
260
304
|
).merge(
|
|
261
|
-
|
|
262
|
-
type:
|
|
305
|
+
z6.object({
|
|
306
|
+
type: z6.enum(["audit", "group"], {
|
|
263
307
|
description: "Discriminant for reference kind, affects where `slug` is looked up"
|
|
264
308
|
}),
|
|
265
309
|
plugin: slugSchema.describe(
|
|
@@ -280,8 +324,8 @@ var categoryConfigSchema = scorableSchema(
|
|
|
280
324
|
description: "Meta info for category"
|
|
281
325
|
})
|
|
282
326
|
).merge(
|
|
283
|
-
|
|
284
|
-
isBinary:
|
|
327
|
+
z6.object({
|
|
328
|
+
isBinary: z6.boolean({
|
|
285
329
|
description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
|
|
286
330
|
}).optional()
|
|
287
331
|
})
|
|
@@ -297,7 +341,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
|
|
|
297
341
|
metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
|
|
298
342
|
);
|
|
299
343
|
}
|
|
300
|
-
var categoriesSchema =
|
|
344
|
+
var categoriesSchema = z6.array(categoryConfigSchema, {
|
|
301
345
|
description: "Categorization of individual audits"
|
|
302
346
|
}).refine(
|
|
303
347
|
(categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
|
|
@@ -316,18 +360,18 @@ function getDuplicateSlugCategories(categories) {
|
|
|
316
360
|
}
|
|
317
361
|
|
|
318
362
|
// packages/models/src/lib/commit.ts
|
|
319
|
-
import { z as
|
|
320
|
-
var commitSchema =
|
|
363
|
+
import { z as z7 } from "zod";
|
|
364
|
+
var commitSchema = z7.object(
|
|
321
365
|
{
|
|
322
|
-
hash:
|
|
366
|
+
hash: z7.string({ description: "Commit SHA (full)" }).regex(
|
|
323
367
|
/^[\da-f]{40}$/,
|
|
324
368
|
"Commit SHA should be a 40-character hexadecimal string"
|
|
325
369
|
),
|
|
326
|
-
message:
|
|
327
|
-
date:
|
|
370
|
+
message: z7.string({ description: "Commit message" }),
|
|
371
|
+
date: z7.coerce.date({
|
|
328
372
|
description: "Date and time when commit was authored"
|
|
329
373
|
}),
|
|
330
|
-
author:
|
|
374
|
+
author: z7.string({
|
|
331
375
|
description: "Commit author name"
|
|
332
376
|
}).trim()
|
|
333
377
|
},
|
|
@@ -335,22 +379,22 @@ var commitSchema = z6.object(
|
|
|
335
379
|
);
|
|
336
380
|
|
|
337
381
|
// packages/models/src/lib/core-config.ts
|
|
338
|
-
import { z as
|
|
382
|
+
import { z as z13 } from "zod";
|
|
339
383
|
|
|
340
384
|
// packages/models/src/lib/persist-config.ts
|
|
341
|
-
import { z as
|
|
342
|
-
var formatSchema =
|
|
343
|
-
var persistConfigSchema =
|
|
385
|
+
import { z as z8 } from "zod";
|
|
386
|
+
var formatSchema = z8.enum(["json", "md"]);
|
|
387
|
+
var persistConfigSchema = z8.object({
|
|
344
388
|
outputDir: filePathSchema.describe("Artifacts folder").optional(),
|
|
345
389
|
filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
|
|
346
|
-
format:
|
|
390
|
+
format: z8.array(formatSchema).optional()
|
|
347
391
|
});
|
|
348
392
|
|
|
349
393
|
// packages/models/src/lib/plugin-config.ts
|
|
350
|
-
import { z as
|
|
394
|
+
import { z as z11 } from "zod";
|
|
351
395
|
|
|
352
396
|
// packages/models/src/lib/group.ts
|
|
353
|
-
import { z as
|
|
397
|
+
import { z as z9 } from "zod";
|
|
354
398
|
var groupRefSchema = weightedRefSchema(
|
|
355
399
|
"Weighted reference to a group",
|
|
356
400
|
"Reference slug to a group within this plugin (e.g. 'max-lines')"
|
|
@@ -367,7 +411,7 @@ var groupSchema = scorableSchema(
|
|
|
367
411
|
getDuplicateRefsInGroups,
|
|
368
412
|
duplicateRefsInGroupsErrorMsg
|
|
369
413
|
).merge(groupMetaSchema);
|
|
370
|
-
var groupsSchema =
|
|
414
|
+
var groupsSchema = z9.array(groupSchema, {
|
|
371
415
|
description: "List of groups"
|
|
372
416
|
}).optional().refine(
|
|
373
417
|
(groups) => !getDuplicateSlugsInGroups(groups),
|
|
@@ -395,14 +439,14 @@ function getDuplicateSlugsInGroups(groups) {
|
|
|
395
439
|
}
|
|
396
440
|
|
|
397
441
|
// packages/models/src/lib/runner-config.ts
|
|
398
|
-
import { z as
|
|
399
|
-
var outputTransformSchema =
|
|
400
|
-
var runnerConfigSchema =
|
|
442
|
+
import { z as z10 } from "zod";
|
|
443
|
+
var outputTransformSchema = z10.function().args(z10.unknown()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
|
|
444
|
+
var runnerConfigSchema = z10.object(
|
|
401
445
|
{
|
|
402
|
-
command:
|
|
446
|
+
command: z10.string({
|
|
403
447
|
description: "Shell command to execute"
|
|
404
448
|
}),
|
|
405
|
-
args:
|
|
449
|
+
args: z10.array(z10.string({ description: "Command arguments" })).optional(),
|
|
406
450
|
outputFile: filePathSchema.describe("Output path"),
|
|
407
451
|
outputTransform: outputTransformSchema.optional()
|
|
408
452
|
},
|
|
@@ -410,8 +454,8 @@ var runnerConfigSchema = z9.object(
|
|
|
410
454
|
description: "How to execute runner"
|
|
411
455
|
}
|
|
412
456
|
);
|
|
413
|
-
var onProgressSchema =
|
|
414
|
-
var runnerFunctionSchema =
|
|
457
|
+
var onProgressSchema = z10.function().args(z10.unknown()).returns(z10.void());
|
|
458
|
+
var runnerFunctionSchema = z10.function().args(onProgressSchema.optional()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
|
|
415
459
|
|
|
416
460
|
// packages/models/src/lib/plugin-config.ts
|
|
417
461
|
var pluginMetaSchema = packageVersionSchema().merge(
|
|
@@ -422,13 +466,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
|
|
|
422
466
|
description: "Plugin metadata"
|
|
423
467
|
})
|
|
424
468
|
).merge(
|
|
425
|
-
|
|
469
|
+
z11.object({
|
|
426
470
|
slug: slugSchema.describe("Unique plugin slug within core config"),
|
|
427
471
|
icon: materialIconSchema
|
|
428
472
|
})
|
|
429
473
|
);
|
|
430
|
-
var pluginDataSchema =
|
|
431
|
-
runner:
|
|
474
|
+
var pluginDataSchema = z11.object({
|
|
475
|
+
runner: z11.union([runnerConfigSchema, runnerFunctionSchema]),
|
|
432
476
|
audits: pluginAuditsSchema,
|
|
433
477
|
groups: groupsSchema
|
|
434
478
|
});
|
|
@@ -454,22 +498,22 @@ function getMissingRefsFromGroups(pluginCfg) {
|
|
|
454
498
|
}
|
|
455
499
|
|
|
456
500
|
// packages/models/src/lib/upload-config.ts
|
|
457
|
-
import { z as
|
|
458
|
-
var uploadConfigSchema =
|
|
501
|
+
import { z as z12 } from "zod";
|
|
502
|
+
var uploadConfigSchema = z12.object({
|
|
459
503
|
server: urlSchema.describe("URL of deployed portal API"),
|
|
460
|
-
apiKey:
|
|
504
|
+
apiKey: z12.string({
|
|
461
505
|
description: "API key with write access to portal (use `process.env` for security)"
|
|
462
506
|
}),
|
|
463
507
|
organization: slugSchema.describe(
|
|
464
508
|
"Organization slug from Code PushUp portal"
|
|
465
509
|
),
|
|
466
510
|
project: slugSchema.describe("Project slug from Code PushUp portal"),
|
|
467
|
-
timeout:
|
|
511
|
+
timeout: z12.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
|
|
468
512
|
});
|
|
469
513
|
|
|
470
514
|
// packages/models/src/lib/core-config.ts
|
|
471
|
-
var unrefinedCoreConfigSchema =
|
|
472
|
-
plugins:
|
|
515
|
+
var unrefinedCoreConfigSchema = z13.object({
|
|
516
|
+
plugins: z13.array(pluginConfigSchema, {
|
|
473
517
|
description: "List of plugins to be used (official, community-provided, or custom)"
|
|
474
518
|
}).min(1),
|
|
475
519
|
/** portal configuration for persisting results */
|
|
@@ -496,7 +540,7 @@ var CONFIG_FILE_NAME = "code-pushup.config";
|
|
|
496
540
|
var SUPPORTED_CONFIG_FILE_FORMATS = ["ts", "mjs", "js"];
|
|
497
541
|
|
|
498
542
|
// packages/models/src/lib/report.ts
|
|
499
|
-
import { z as
|
|
543
|
+
import { z as z14 } from "zod";
|
|
500
544
|
var auditReportSchema = auditSchema.merge(auditOutputSchema);
|
|
501
545
|
var pluginReportSchema = pluginMetaSchema.merge(
|
|
502
546
|
executionMetaSchema({
|
|
@@ -504,9 +548,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
|
|
|
504
548
|
descriptionDuration: "Duration of the plugin run in ms"
|
|
505
549
|
})
|
|
506
550
|
).merge(
|
|
507
|
-
|
|
508
|
-
audits:
|
|
509
|
-
groups:
|
|
551
|
+
z14.object({
|
|
552
|
+
audits: z14.array(auditReportSchema).min(1),
|
|
553
|
+
groups: z14.array(groupSchema).optional()
|
|
510
554
|
})
|
|
511
555
|
).refine(
|
|
512
556
|
(pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
|
|
@@ -540,10 +584,10 @@ var reportSchema = packageVersionSchema({
|
|
|
540
584
|
descriptionDuration: "Duration of the collect run in ms"
|
|
541
585
|
})
|
|
542
586
|
).merge(
|
|
543
|
-
|
|
587
|
+
z14.object(
|
|
544
588
|
{
|
|
545
|
-
categories:
|
|
546
|
-
plugins:
|
|
589
|
+
categories: z14.array(categoryConfigSchema),
|
|
590
|
+
plugins: z14.array(pluginReportSchema).min(1),
|
|
547
591
|
commit: commitSchema.describe("Git commit for which report was collected").nullable()
|
|
548
592
|
},
|
|
549
593
|
{ description: "Collect output data" }
|
|
@@ -559,36 +603,40 @@ var reportSchema = packageVersionSchema({
|
|
|
559
603
|
);
|
|
560
604
|
|
|
561
605
|
// packages/models/src/lib/reports-diff.ts
|
|
562
|
-
import { z as
|
|
606
|
+
import { z as z15 } from "zod";
|
|
563
607
|
function makeComparisonSchema(schema) {
|
|
564
608
|
const sharedDescription = schema.description || "Result";
|
|
565
|
-
return
|
|
609
|
+
return z15.object({
|
|
566
610
|
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
567
611
|
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
568
612
|
});
|
|
569
613
|
}
|
|
570
614
|
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
571
|
-
return
|
|
615
|
+
return z15.object(
|
|
572
616
|
{
|
|
573
|
-
changed:
|
|
574
|
-
unchanged:
|
|
575
|
-
added:
|
|
576
|
-
removed:
|
|
617
|
+
changed: z15.array(diffSchema),
|
|
618
|
+
unchanged: z15.array(resultSchema),
|
|
619
|
+
added: z15.array(resultSchema),
|
|
620
|
+
removed: z15.array(resultSchema)
|
|
577
621
|
},
|
|
578
622
|
{ description }
|
|
579
623
|
);
|
|
580
624
|
}
|
|
581
|
-
var scorableMetaSchema =
|
|
625
|
+
var scorableMetaSchema = z15.object({
|
|
626
|
+
slug: slugSchema,
|
|
627
|
+
title: titleSchema,
|
|
628
|
+
docsUrl: docsUrlSchema
|
|
629
|
+
});
|
|
582
630
|
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
583
|
-
|
|
584
|
-
plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
|
|
631
|
+
z15.object({
|
|
632
|
+
plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
|
|
585
633
|
})
|
|
586
634
|
);
|
|
587
635
|
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
588
|
-
|
|
636
|
+
z15.object({
|
|
589
637
|
scores: makeComparisonSchema(scoreSchema).merge(
|
|
590
|
-
|
|
591
|
-
diff:
|
|
638
|
+
z15.object({
|
|
639
|
+
diff: z15.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
592
640
|
})
|
|
593
641
|
).describe("Score comparison")
|
|
594
642
|
})
|
|
@@ -599,10 +647,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
|
599
647
|
var categoryDiffSchema = scorableDiffSchema;
|
|
600
648
|
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
601
649
|
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
602
|
-
|
|
650
|
+
z15.object({
|
|
603
651
|
values: makeComparisonSchema(auditValueSchema).merge(
|
|
604
|
-
|
|
605
|
-
diff:
|
|
652
|
+
z15.object({
|
|
653
|
+
diff: z15.number().int().describe("Value change (`values.after - values.before`)")
|
|
606
654
|
})
|
|
607
655
|
).describe("Audit `value` comparison"),
|
|
608
656
|
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
@@ -611,15 +659,15 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
|
611
659
|
})
|
|
612
660
|
);
|
|
613
661
|
var categoryResultSchema = scorableMetaSchema.merge(
|
|
614
|
-
|
|
662
|
+
z15.object({ score: scoreSchema })
|
|
615
663
|
);
|
|
616
664
|
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
617
|
-
|
|
665
|
+
z15.object({ score: scoreSchema })
|
|
618
666
|
);
|
|
619
667
|
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
620
668
|
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
621
669
|
);
|
|
622
|
-
var reportsDiffSchema =
|
|
670
|
+
var reportsDiffSchema = z15.object({
|
|
623
671
|
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
624
672
|
categories: makeArraysComparisonSchema(
|
|
625
673
|
categoryDiffSchema,
|
|
@@ -648,6 +696,288 @@ var reportsDiffSchema = z14.object({
|
|
|
648
696
|
})
|
|
649
697
|
);
|
|
650
698
|
|
|
699
|
+
// packages/utils/src/lib/text-formats/constants.ts
|
|
700
|
+
var NEW_LINE = "\n";
|
|
701
|
+
var TAB = " ";
|
|
702
|
+
var SPACE = " ";
|
|
703
|
+
|
|
704
|
+
// packages/utils/src/lib/text-formats/html/details.ts
|
|
705
|
+
function details(title, content, cfg = { open: false }) {
|
|
706
|
+
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.
|
|
707
|
+
NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
|
|
708
|
+
// ⚠️ The blank line ensure Markdown in content is rendered correctly.
|
|
709
|
+
NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
|
|
710
|
+
NEW_LINE}`;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// packages/utils/src/lib/text-formats/html/font-style.ts
|
|
714
|
+
var boldElement = "b";
|
|
715
|
+
function bold(text) {
|
|
716
|
+
return `<${boldElement}>${text}</${boldElement}>`;
|
|
717
|
+
}
|
|
718
|
+
var italicElement = "i";
|
|
719
|
+
function italic(text) {
|
|
720
|
+
return `<${italicElement}>${text}</${italicElement}>`;
|
|
721
|
+
}
|
|
722
|
+
var codeElement = "code";
|
|
723
|
+
function code(text) {
|
|
724
|
+
return `<${codeElement}>${text}</${codeElement}>`;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// packages/utils/src/lib/text-formats/html/link.ts
|
|
728
|
+
function link(href, text) {
|
|
729
|
+
return `<a href="${href}">${text || href}"</a>`;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// packages/utils/src/lib/transform.ts
|
|
733
|
+
function objectToEntries(obj) {
|
|
734
|
+
return Object.entries(obj);
|
|
735
|
+
}
|
|
736
|
+
function deepClone(obj) {
|
|
737
|
+
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
738
|
+
}
|
|
739
|
+
function toUnixPath(path) {
|
|
740
|
+
return path.replace(/\\/g, "/");
|
|
741
|
+
}
|
|
742
|
+
function capitalize(text) {
|
|
743
|
+
return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
|
|
744
|
+
1
|
|
745
|
+
)}`;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// packages/utils/src/lib/table.ts
|
|
749
|
+
function rowToStringArray({ rows, columns = [] }) {
|
|
750
|
+
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
751
|
+
throw new TypeError(
|
|
752
|
+
"Column can`t be object when rows are primitive values"
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
return rows.map((row) => {
|
|
756
|
+
if (Array.isArray(row)) {
|
|
757
|
+
return row.map(String);
|
|
758
|
+
}
|
|
759
|
+
const objectRow = row;
|
|
760
|
+
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
761
|
+
return Object.values(objectRow).map(String);
|
|
762
|
+
}
|
|
763
|
+
return columns.map(
|
|
764
|
+
({ key }) => String(objectRow[key])
|
|
765
|
+
);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
function columnsToStringArray({ rows, columns = [] }) {
|
|
769
|
+
const firstRow = rows.at(0);
|
|
770
|
+
const primitiveRows = Array.isArray(firstRow);
|
|
771
|
+
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
772
|
+
throw new Error("invalid union type. Caught by model parsing.");
|
|
773
|
+
}
|
|
774
|
+
if (columns.length === 0) {
|
|
775
|
+
if (Array.isArray(firstRow)) {
|
|
776
|
+
return firstRow.map((_, idx) => String(idx));
|
|
777
|
+
}
|
|
778
|
+
return Object.keys(firstRow);
|
|
779
|
+
}
|
|
780
|
+
if (typeof columns.at(0) === "string") {
|
|
781
|
+
return columns.map(String);
|
|
782
|
+
}
|
|
783
|
+
const cols = columns;
|
|
784
|
+
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
785
|
+
}
|
|
786
|
+
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
787
|
+
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
788
|
+
if (typeof column === "string") {
|
|
789
|
+
return column;
|
|
790
|
+
} else if (typeof column === "object") {
|
|
791
|
+
return column.align ?? "center";
|
|
792
|
+
} else {
|
|
793
|
+
return "center";
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
797
|
+
const column = columns.at(targetIdx);
|
|
798
|
+
if (column == null) {
|
|
799
|
+
return "center";
|
|
800
|
+
} else if (typeof column === "string") {
|
|
801
|
+
return column;
|
|
802
|
+
} else if (typeof column === "object") {
|
|
803
|
+
return column.align ?? "center";
|
|
804
|
+
} else {
|
|
805
|
+
return "center";
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function getColumnAlignments({
|
|
809
|
+
rows,
|
|
810
|
+
columns = []
|
|
811
|
+
}) {
|
|
812
|
+
if (rows.at(0) == null) {
|
|
813
|
+
throw new Error("first row can`t be undefined.");
|
|
814
|
+
}
|
|
815
|
+
if (Array.isArray(rows.at(0))) {
|
|
816
|
+
const firstPrimitiveRow = rows.at(0);
|
|
817
|
+
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
818
|
+
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
const firstObject = rows.at(0);
|
|
822
|
+
return Object.keys(firstObject).map(
|
|
823
|
+
(key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// packages/utils/src/lib/text-formats/html/table.ts
|
|
828
|
+
function wrap(elem, content) {
|
|
829
|
+
return `<${elem}>${content}</${elem}>${NEW_LINE}`;
|
|
830
|
+
}
|
|
831
|
+
function wrapRow(content) {
|
|
832
|
+
const elem = "tr";
|
|
833
|
+
return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
|
|
834
|
+
}
|
|
835
|
+
function table(tableData) {
|
|
836
|
+
if (tableData.rows.length === 0) {
|
|
837
|
+
throw new Error("Data can't be empty");
|
|
838
|
+
}
|
|
839
|
+
const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
|
|
840
|
+
const tableHeaderRow = wrapRow(tableHeaderCols);
|
|
841
|
+
const tableBody = rowToStringArray(tableData).map((arr) => {
|
|
842
|
+
const columns = arr.map((s) => wrap("td", s)).join("");
|
|
843
|
+
return wrapRow(columns);
|
|
844
|
+
}).join("");
|
|
845
|
+
return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// packages/utils/src/lib/text-formats/md/font-style.ts
|
|
849
|
+
var boldWrap = "**";
|
|
850
|
+
function bold2(text) {
|
|
851
|
+
return `${boldWrap}${text}${boldWrap}`;
|
|
852
|
+
}
|
|
853
|
+
var italicWrap = "_";
|
|
854
|
+
function italic2(text) {
|
|
855
|
+
return `${italicWrap}${text}${italicWrap}`;
|
|
856
|
+
}
|
|
857
|
+
var strikeThroughWrap = "~";
|
|
858
|
+
function strikeThrough(text) {
|
|
859
|
+
return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
|
|
860
|
+
}
|
|
861
|
+
var codeWrap = "`";
|
|
862
|
+
function code2(text) {
|
|
863
|
+
return `${codeWrap}${text}${codeWrap}`;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// packages/utils/src/lib/text-formats/md/headline.ts
|
|
867
|
+
function headline(text, hierarchy = 1) {
|
|
868
|
+
return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
|
|
869
|
+
}
|
|
870
|
+
function h(text, hierarchy = 1) {
|
|
871
|
+
return headline(text, hierarchy);
|
|
872
|
+
}
|
|
873
|
+
function h1(text) {
|
|
874
|
+
return headline(text, 1);
|
|
875
|
+
}
|
|
876
|
+
function h2(text) {
|
|
877
|
+
return headline(text, 2);
|
|
878
|
+
}
|
|
879
|
+
function h3(text) {
|
|
880
|
+
return headline(text, 3);
|
|
881
|
+
}
|
|
882
|
+
function h4(text) {
|
|
883
|
+
return headline(text, 4);
|
|
884
|
+
}
|
|
885
|
+
function h5(text) {
|
|
886
|
+
return headline(text, 5);
|
|
887
|
+
}
|
|
888
|
+
function h6(text) {
|
|
889
|
+
return headline(text, 6);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// packages/utils/src/lib/text-formats/md/image.ts
|
|
893
|
+
function image(src, alt) {
|
|
894
|
+
return ``;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// packages/utils/src/lib/text-formats/md/link.ts
|
|
898
|
+
function link2(href, text) {
|
|
899
|
+
return `[${text || href}](${href})`;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// packages/utils/src/lib/text-formats/md/list.ts
|
|
903
|
+
function li(text, order = "unordered") {
|
|
904
|
+
const style = order === "unordered" ? "-" : "- [ ]";
|
|
905
|
+
return `${style} ${text}`;
|
|
906
|
+
}
|
|
907
|
+
function indentation(text, level = 1) {
|
|
908
|
+
return `${TAB.repeat(level)}${text}`;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// packages/utils/src/lib/text-formats/md/paragraphs.ts
|
|
912
|
+
function paragraphs(...sections) {
|
|
913
|
+
return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// packages/utils/src/lib/text-formats/md/section.ts
|
|
917
|
+
function section(...contents) {
|
|
918
|
+
return `${lines(...contents)}${NEW_LINE}`;
|
|
919
|
+
}
|
|
920
|
+
function lines(...contents) {
|
|
921
|
+
return `${contents.filter(Boolean).join(NEW_LINE)}`;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// packages/utils/src/lib/text-formats/md/table.ts
|
|
925
|
+
var alignString = /* @__PURE__ */ new Map([
|
|
926
|
+
["left", ":--"],
|
|
927
|
+
["center", ":--:"],
|
|
928
|
+
["right", "--:"]
|
|
929
|
+
]);
|
|
930
|
+
function tableRow(rows) {
|
|
931
|
+
return `|${rows.join("|")}|`;
|
|
932
|
+
}
|
|
933
|
+
function table2(data) {
|
|
934
|
+
if (data.rows.length === 0) {
|
|
935
|
+
throw new Error("Data can't be empty");
|
|
936
|
+
}
|
|
937
|
+
const alignmentRow = getColumnAlignments(data).map(
|
|
938
|
+
(s) => alignString.get(s) ?? String(alignString.get("center"))
|
|
939
|
+
);
|
|
940
|
+
return section(
|
|
941
|
+
`${lines(
|
|
942
|
+
tableRow(columnsToStringArray(data)),
|
|
943
|
+
tableRow(alignmentRow),
|
|
944
|
+
...rowToStringArray(data).map(tableRow)
|
|
945
|
+
)}`
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// packages/utils/src/lib/text-formats/index.ts
|
|
950
|
+
var md = {
|
|
951
|
+
bold: bold2,
|
|
952
|
+
italic: italic2,
|
|
953
|
+
strikeThrough,
|
|
954
|
+
code: code2,
|
|
955
|
+
link: link2,
|
|
956
|
+
image,
|
|
957
|
+
headline,
|
|
958
|
+
h,
|
|
959
|
+
h1,
|
|
960
|
+
h2,
|
|
961
|
+
h3,
|
|
962
|
+
h4,
|
|
963
|
+
h5,
|
|
964
|
+
h6,
|
|
965
|
+
indentation,
|
|
966
|
+
lines,
|
|
967
|
+
li,
|
|
968
|
+
section,
|
|
969
|
+
paragraphs,
|
|
970
|
+
table: table2
|
|
971
|
+
};
|
|
972
|
+
var html = {
|
|
973
|
+
bold,
|
|
974
|
+
italic,
|
|
975
|
+
code,
|
|
976
|
+
link,
|
|
977
|
+
details,
|
|
978
|
+
table
|
|
979
|
+
};
|
|
980
|
+
|
|
651
981
|
// packages/utils/src/lib/diff.ts
|
|
652
982
|
function matchArrayItemsByKey({
|
|
653
983
|
before,
|
|
@@ -765,40 +1095,48 @@ import chalk from "chalk";
|
|
|
765
1095
|
|
|
766
1096
|
// packages/utils/src/lib/reports/constants.ts
|
|
767
1097
|
var TERMINAL_WIDTH = 80;
|
|
768
|
-
var NEW_LINE = "\n";
|
|
769
1098
|
var SCORE_COLOR_RANGE = {
|
|
770
1099
|
GREEN_MIN: 0.9,
|
|
771
1100
|
YELLOW_MIN: 0.5
|
|
772
1101
|
};
|
|
1102
|
+
var CATEGORIES_TITLE = "\u{1F3F7} Categories";
|
|
773
1103
|
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
774
1104
|
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
775
|
-
var README_LINK = "https://github.com/
|
|
1105
|
+
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
776
1106
|
var reportHeadlineText = "Code PushUp Report";
|
|
777
1107
|
var reportOverviewTableHeaders = [
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1108
|
+
{
|
|
1109
|
+
key: "category",
|
|
1110
|
+
label: "\u{1F3F7} Category",
|
|
1111
|
+
align: "left"
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
key: "score",
|
|
1115
|
+
label: "\u2B50 Score"
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
key: "audits",
|
|
1119
|
+
label: "\u{1F6E1} Audits"
|
|
1120
|
+
}
|
|
781
1121
|
];
|
|
782
1122
|
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
783
|
-
var
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
"Source file",
|
|
801
|
-
"Line(s)"
|
|
1123
|
+
var issuesTableHeadings = [
|
|
1124
|
+
{
|
|
1125
|
+
key: "severity",
|
|
1126
|
+
label: "Severity"
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
key: "message",
|
|
1130
|
+
label: "Message"
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
key: "file",
|
|
1134
|
+
label: "Source file"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
key: "line",
|
|
1138
|
+
label: "Line(s)"
|
|
1139
|
+
}
|
|
802
1140
|
];
|
|
803
1141
|
|
|
804
1142
|
// packages/utils/src/lib/logging.ts
|
|
@@ -888,7 +1226,7 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
888
1226
|
await mkdir(baseDir, { recursive: true });
|
|
889
1227
|
return;
|
|
890
1228
|
} catch (error) {
|
|
891
|
-
ui().logger.
|
|
1229
|
+
ui().logger.info(error.message);
|
|
892
1230
|
if (error.code !== "EEXIST") {
|
|
893
1231
|
throw error;
|
|
894
1232
|
}
|
|
@@ -924,122 +1262,35 @@ async function importEsmModule(options) {
|
|
|
924
1262
|
return mod.default;
|
|
925
1263
|
}
|
|
926
1264
|
|
|
927
|
-
// packages/utils/src/lib/reports/md/details.ts
|
|
928
|
-
function details(title, content, cfg = { open: false }) {
|
|
929
|
-
return `<details${cfg.open ? " open" : ""}>
|
|
930
|
-
<summary>${title}</summary>
|
|
931
|
-
|
|
932
|
-
${content}
|
|
933
|
-
|
|
934
|
-
</details>
|
|
935
|
-
`;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
// packages/utils/src/lib/reports/md/font-style.ts
|
|
939
|
-
var stylesMap = {
|
|
940
|
-
i: "_",
|
|
941
|
-
// italic
|
|
942
|
-
b: "**",
|
|
943
|
-
// bold
|
|
944
|
-
s: "~",
|
|
945
|
-
// strike through
|
|
946
|
-
c: "`"
|
|
947
|
-
// code
|
|
948
|
-
};
|
|
949
|
-
function style(text, styles = ["b"]) {
|
|
950
|
-
return styles.reduce((t, s) => `${stylesMap[s]}${t}${stylesMap[s]}`, text);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
// packages/utils/src/lib/reports/md/headline.ts
|
|
954
|
-
function headline(text, hierarchy = 1) {
|
|
955
|
-
return `${"#".repeat(hierarchy)} ${text}`;
|
|
956
|
-
}
|
|
957
|
-
function h1(text) {
|
|
958
|
-
return headline(text, 1);
|
|
959
|
-
}
|
|
960
|
-
function h2(text) {
|
|
961
|
-
return headline(text, 2);
|
|
962
|
-
}
|
|
963
|
-
function h3(text) {
|
|
964
|
-
return headline(text, 3);
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// packages/utils/src/lib/reports/md/image.ts
|
|
968
|
-
function image(src, alt) {
|
|
969
|
-
return ``;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// packages/utils/src/lib/reports/md/link.ts
|
|
973
|
-
function link(href, text) {
|
|
974
|
-
return `[${text || href}](${href})`;
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
// packages/utils/src/lib/reports/md/list.ts
|
|
978
|
-
function li(text, order = "unordered") {
|
|
979
|
-
const style2 = order === "unordered" ? "-" : "- [ ]";
|
|
980
|
-
return `${style2} ${text}`;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// packages/utils/src/lib/reports/md/paragraphs.ts
|
|
984
|
-
function paragraphs(...sections) {
|
|
985
|
-
return sections.filter(Boolean).join("\n\n");
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
// packages/utils/src/lib/reports/md/table.ts
|
|
989
|
-
var alignString = /* @__PURE__ */ new Map([
|
|
990
|
-
["l", ":--"],
|
|
991
|
-
["c", ":--:"],
|
|
992
|
-
["r", "--:"]
|
|
993
|
-
]);
|
|
994
|
-
function tableMd(data, align) {
|
|
995
|
-
if (data.length === 0) {
|
|
996
|
-
throw new Error("Data can't be empty");
|
|
997
|
-
}
|
|
998
|
-
const alignmentSetting = align ?? data[0]?.map(() => "c");
|
|
999
|
-
const tableContent = data.map((arr) => `|${arr.join("|")}|`);
|
|
1000
|
-
const alignmentRow = `|${alignmentSetting?.map((s) => alignString.get(s)).join("|")}|`;
|
|
1001
|
-
return tableContent[0] + NEW_LINE + alignmentRow + NEW_LINE + tableContent.slice(1).join(NEW_LINE);
|
|
1002
|
-
}
|
|
1003
|
-
function tableHtml(data) {
|
|
1004
|
-
if (data.length === 0) {
|
|
1005
|
-
throw new Error("Data can't be empty");
|
|
1006
|
-
}
|
|
1007
|
-
const tableContent = data.map((arr, index) => {
|
|
1008
|
-
if (index === 0) {
|
|
1009
|
-
const headerRow = arr.map((s) => `<th>${s}</th>`).join("");
|
|
1010
|
-
return `<tr>${headerRow}</tr>`;
|
|
1011
|
-
}
|
|
1012
|
-
const row = arr.map((s) => `<td>${s}</td>`).join("");
|
|
1013
|
-
return `<tr>${row}</tr>`;
|
|
1014
|
-
});
|
|
1015
|
-
return `<table>${tableContent.join("")}</table>`;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
1265
|
// packages/utils/src/lib/reports/utils.ts
|
|
1266
|
+
var { image: image2, bold: boldMd } = md;
|
|
1019
1267
|
function formatReportScore(score) {
|
|
1020
1268
|
return Math.round(score * 100).toString();
|
|
1021
1269
|
}
|
|
1022
1270
|
function formatScoreWithColor(score, options) {
|
|
1023
|
-
const styledNumber = options?.skipBold ? formatReportScore(score) :
|
|
1024
|
-
return `${
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1271
|
+
const styledNumber = options?.skipBold ? formatReportScore(score) : boldMd(formatReportScore(score));
|
|
1272
|
+
return `${scoreMarker(score)} ${styledNumber}`;
|
|
1273
|
+
}
|
|
1274
|
+
var MARKERS = {
|
|
1275
|
+
circle: {
|
|
1276
|
+
red: "\u{1F534}",
|
|
1277
|
+
yellow: "\u{1F7E1}",
|
|
1278
|
+
green: "\u{1F7E2}"
|
|
1279
|
+
},
|
|
1280
|
+
square: {
|
|
1281
|
+
red: "\u{1F7E5}",
|
|
1282
|
+
yellow: "\u{1F7E8}",
|
|
1283
|
+
green: "\u{1F7E9}"
|
|
1032
1284
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
function getSquaredScoreMarker(score) {
|
|
1285
|
+
};
|
|
1286
|
+
function scoreMarker(score, markerType = "circle") {
|
|
1036
1287
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1037
|
-
return
|
|
1288
|
+
return MARKERS[markerType].green;
|
|
1038
1289
|
}
|
|
1039
1290
|
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1040
|
-
return
|
|
1291
|
+
return MARKERS[markerType].yellow;
|
|
1041
1292
|
}
|
|
1042
|
-
return
|
|
1293
|
+
return MARKERS[markerType].red;
|
|
1043
1294
|
}
|
|
1044
1295
|
function getDiffMarker(diff) {
|
|
1045
1296
|
if (diff > 0) {
|
|
@@ -1055,7 +1306,7 @@ function colorByScoreDiff(text, diff) {
|
|
|
1055
1306
|
return shieldsBadge(text, color);
|
|
1056
1307
|
}
|
|
1057
1308
|
function shieldsBadge(text, color) {
|
|
1058
|
-
return
|
|
1309
|
+
return image2(
|
|
1059
1310
|
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1060
1311
|
text
|
|
1061
1312
|
);
|
|
@@ -1065,7 +1316,7 @@ function formatDiffNumber(diff) {
|
|
|
1065
1316
|
const sign = diff < 0 ? "\u2212" : "+";
|
|
1066
1317
|
return `${sign}${number}`;
|
|
1067
1318
|
}
|
|
1068
|
-
function
|
|
1319
|
+
function severityMarker(severity) {
|
|
1069
1320
|
if (severity === "error") {
|
|
1070
1321
|
return "\u{1F6A8}";
|
|
1071
1322
|
}
|
|
@@ -1256,13 +1507,13 @@ function executeProcess(cfg) {
|
|
|
1256
1507
|
process2.on("error", (err) => {
|
|
1257
1508
|
stderr += err.toString();
|
|
1258
1509
|
});
|
|
1259
|
-
process2.on("close", (
|
|
1510
|
+
process2.on("close", (code3) => {
|
|
1260
1511
|
const timings = { date, duration: calcDuration(start) };
|
|
1261
|
-
if (
|
|
1512
|
+
if (code3 === 0 || ignoreExitCode) {
|
|
1262
1513
|
onComplete?.();
|
|
1263
|
-
resolve({ code, stdout, stderr, ...timings });
|
|
1514
|
+
resolve({ code: code3, stdout, stderr, ...timings });
|
|
1264
1515
|
} else {
|
|
1265
|
-
const errorMsg = new ProcessError({ code, stdout, stderr, ...timings });
|
|
1516
|
+
const errorMsg = new ProcessError({ code: code3, stdout, stderr, ...timings });
|
|
1266
1517
|
onError?.(errorMsg);
|
|
1267
1518
|
reject(errorMsg);
|
|
1268
1519
|
}
|
|
@@ -1270,39 +1521,84 @@ function executeProcess(cfg) {
|
|
|
1270
1521
|
});
|
|
1271
1522
|
}
|
|
1272
1523
|
|
|
1273
|
-
// packages/utils/src/lib/git.ts
|
|
1524
|
+
// packages/utils/src/lib/git/git.ts
|
|
1274
1525
|
import { isAbsolute, join as join2, relative } from "node:path";
|
|
1275
1526
|
import { simpleGit } from "simple-git";
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
function objectToEntries(obj) {
|
|
1279
|
-
return Object.entries(obj);
|
|
1527
|
+
function getGitRoot(git = simpleGit()) {
|
|
1528
|
+
return git.revparse("--show-toplevel");
|
|
1280
1529
|
}
|
|
1281
|
-
function
|
|
1282
|
-
|
|
1530
|
+
function formatGitPath(path, gitRoot) {
|
|
1531
|
+
const absolutePath = isAbsolute(path) ? path : join2(process.cwd(), path);
|
|
1532
|
+
const relativePath = relative(gitRoot, absolutePath);
|
|
1533
|
+
return toUnixPath(relativePath);
|
|
1283
1534
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1535
|
+
var GitStatusError = class _GitStatusError extends Error {
|
|
1536
|
+
static ignoredProps = /* @__PURE__ */ new Set(["current", "tracking"]);
|
|
1537
|
+
static getReducedStatus(status) {
|
|
1538
|
+
return Object.fromEntries(
|
|
1539
|
+
Object.entries(status).filter(([key]) => !this.ignoredProps.has(key)).filter(
|
|
1540
|
+
(entry) => {
|
|
1541
|
+
const value = entry[1];
|
|
1542
|
+
if (value == null) {
|
|
1543
|
+
return false;
|
|
1544
|
+
}
|
|
1545
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
if (typeof value === "number" && value === 0) {
|
|
1549
|
+
return false;
|
|
1550
|
+
}
|
|
1551
|
+
return !(typeof value === "boolean" && !value);
|
|
1552
|
+
}
|
|
1553
|
+
)
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
constructor(status) {
|
|
1557
|
+
super(
|
|
1558
|
+
`Working directory needs to be clean before we you can proceed. Commit your local changes or stash them:
|
|
1559
|
+
${JSON.stringify(
|
|
1560
|
+
_GitStatusError.getReducedStatus(status),
|
|
1561
|
+
null,
|
|
1562
|
+
2
|
|
1563
|
+
)}`
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
async function guardAgainstLocalChanges(git = simpleGit()) {
|
|
1568
|
+
const status = await git.status(["-s"]);
|
|
1569
|
+
if (status.files.length > 0) {
|
|
1570
|
+
throw new GitStatusError(status);
|
|
1571
|
+
}
|
|
1286
1572
|
}
|
|
1573
|
+
async function safeCheckout(branchOrHash, forceCleanStatus = false, git = simpleGit()) {
|
|
1574
|
+
if (forceCleanStatus) {
|
|
1575
|
+
await git.raw(["reset", "--hard"]);
|
|
1576
|
+
await git.clean(["f", "d"]);
|
|
1577
|
+
ui().logger.info(`git status cleaned`);
|
|
1578
|
+
}
|
|
1579
|
+
await guardAgainstLocalChanges(git);
|
|
1580
|
+
await git.checkout(branchOrHash);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
// packages/utils/src/lib/git/git.commits-and-tags.ts
|
|
1584
|
+
import { simpleGit as simpleGit2 } from "simple-git";
|
|
1585
|
+
|
|
1586
|
+
// packages/utils/src/lib/semver.ts
|
|
1587
|
+
import { rcompare, valid } from "semver";
|
|
1287
1588
|
|
|
1288
|
-
// packages/utils/src/lib/git.ts
|
|
1289
|
-
async function getLatestCommit(git =
|
|
1589
|
+
// packages/utils/src/lib/git/git.commits-and-tags.ts
|
|
1590
|
+
async function getLatestCommit(git = simpleGit2()) {
|
|
1290
1591
|
const log2 = await git.log({
|
|
1291
1592
|
maxCount: 1,
|
|
1593
|
+
// git log -1 --pretty=format:"%H %s %an %aI" - See: https://git-scm.com/docs/pretty-formats
|
|
1292
1594
|
format: { hash: "%H", message: "%s", author: "%an", date: "%aI" }
|
|
1293
1595
|
});
|
|
1294
|
-
if (!log2.latest) {
|
|
1295
|
-
return null;
|
|
1296
|
-
}
|
|
1297
1596
|
return commitSchema.parse(log2.latest);
|
|
1298
1597
|
}
|
|
1299
|
-
function
|
|
1300
|
-
return git.
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
const absolutePath = isAbsolute(path) ? path : join2(process.cwd(), path);
|
|
1304
|
-
const relativePath = relative(gitRoot, absolutePath);
|
|
1305
|
-
return toUnixPath(relativePath);
|
|
1598
|
+
async function getCurrentBranchOrTag(git = simpleGit2()) {
|
|
1599
|
+
return await git.branch().then((r) => r.current) || // If no current branch, try to get the tag
|
|
1600
|
+
// @TODO use simple git
|
|
1601
|
+
await git.raw(["describe", "--tags", "--exact-match"]).then((out) => out.trim());
|
|
1306
1602
|
}
|
|
1307
1603
|
|
|
1308
1604
|
// packages/utils/src/lib/group-by-status.ts
|
|
@@ -1380,44 +1676,65 @@ function listAuditsFromAllPlugins(report) {
|
|
|
1380
1676
|
);
|
|
1381
1677
|
}
|
|
1382
1678
|
|
|
1383
|
-
// packages/utils/src/lib/reports/
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
`${FOOTER_PREFIX} ${link(README_LINK, "Code PushUp")}`
|
|
1679
|
+
// packages/utils/src/lib/reports/formatting.ts
|
|
1680
|
+
var { headline: headline2, lines: lines2, link: link3, section: section2, table: table3 } = md;
|
|
1681
|
+
function tableSection(tableData, options) {
|
|
1682
|
+
if (tableData.rows.length === 0) {
|
|
1683
|
+
return "";
|
|
1684
|
+
}
|
|
1685
|
+
const { level = 4 } = options ?? {};
|
|
1686
|
+
const render = (h7, l) => l === 0 ? h7 : headline2(h7, l);
|
|
1687
|
+
return lines2(
|
|
1688
|
+
tableData.title && render(tableData.title, level),
|
|
1689
|
+
table3(tableData)
|
|
1395
1690
|
);
|
|
1396
1691
|
}
|
|
1397
|
-
function
|
|
1398
|
-
|
|
1692
|
+
function metaDescription({
|
|
1693
|
+
docsUrl,
|
|
1694
|
+
description
|
|
1695
|
+
}) {
|
|
1696
|
+
if (docsUrl) {
|
|
1697
|
+
const docsLink = link3(docsUrl, "\u{1F4D6} Docs");
|
|
1698
|
+
if (!description) {
|
|
1699
|
+
return section2(docsLink);
|
|
1700
|
+
}
|
|
1701
|
+
const parsedDescription = description.toString().endsWith("```") ? `${description}${NEW_LINE + NEW_LINE}` : `${description}${SPACE}`;
|
|
1702
|
+
return section2(`${parsedDescription}${docsLink}`);
|
|
1703
|
+
}
|
|
1704
|
+
if (description && description.trim().length > 0) {
|
|
1705
|
+
return section2(description);
|
|
1706
|
+
}
|
|
1707
|
+
return "";
|
|
1399
1708
|
}
|
|
1400
|
-
|
|
1709
|
+
|
|
1710
|
+
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
1711
|
+
var { link: link4, section: section3, h2: h22, lines: lines3, li: li2, bold: boldMd2, h3: h32, indentation: indentation2 } = md;
|
|
1712
|
+
function categoriesOverviewSection(report) {
|
|
1401
1713
|
const { categories, plugins } = report;
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1714
|
+
if (categories.length > 0 && plugins.length > 0) {
|
|
1715
|
+
const tableContent = {
|
|
1716
|
+
columns: reportOverviewTableHeaders,
|
|
1717
|
+
rows: categories.map(({ title, refs, score }) => ({
|
|
1718
|
+
// The heading "ID" is inferred from the heading text in Markdown.
|
|
1719
|
+
category: link4(`#${slugify(title)}`, title),
|
|
1720
|
+
score: `${scoreMarker(score)}${SPACE}${boldMd2(
|
|
1721
|
+
formatReportScore(score)
|
|
1722
|
+
)}`,
|
|
1723
|
+
audits: countCategoryAudits(refs, plugins).toString()
|
|
1724
|
+
}))
|
|
1725
|
+
};
|
|
1726
|
+
return tableSection(tableContent);
|
|
1727
|
+
}
|
|
1728
|
+
return "";
|
|
1411
1729
|
}
|
|
1412
|
-
function
|
|
1730
|
+
function categoriesDetailsSection(report) {
|
|
1413
1731
|
const { categories, plugins } = report;
|
|
1414
|
-
const categoryDetails = categories.
|
|
1415
|
-
const categoryTitle =
|
|
1416
|
-
const categoryScore = `${
|
|
1732
|
+
const categoryDetails = categories.flatMap((category) => {
|
|
1733
|
+
const categoryTitle = h32(category.title);
|
|
1734
|
+
const categoryScore = `${scoreMarker(
|
|
1417
1735
|
category.score
|
|
1418
|
-
)}
|
|
1419
|
-
const
|
|
1420
|
-
const categoryMDItems = category.refs.reduce((refAcc, ref) => {
|
|
1736
|
+
)}${SPACE}Score: ${boldMd2(formatReportScore(category.score))}`;
|
|
1737
|
+
const categoryMDItems = category.refs.map((ref) => {
|
|
1421
1738
|
if (ref.type === "group") {
|
|
1422
1739
|
const group = getSortableGroupByRef(ref, plugins);
|
|
1423
1740
|
const groupAudits = group.refs.map(
|
|
@@ -1427,151 +1744,240 @@ function reportToCategoriesSection(report) {
|
|
|
1427
1744
|
)
|
|
1428
1745
|
);
|
|
1429
1746
|
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1430
|
-
|
|
1431
|
-
group,
|
|
1432
|
-
groupAudits,
|
|
1433
|
-
pluginTitle
|
|
1434
|
-
);
|
|
1435
|
-
return refAcc + mdGroupItem + NEW_LINE;
|
|
1747
|
+
return categoryGroupItem(group, groupAudits, pluginTitle);
|
|
1436
1748
|
} else {
|
|
1437
1749
|
const audit = getSortableAuditByRef(ref, plugins);
|
|
1438
1750
|
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1439
|
-
|
|
1440
|
-
return refAcc + mdAuditItem + NEW_LINE;
|
|
1751
|
+
return categoryRef(audit, pluginTitle);
|
|
1441
1752
|
}
|
|
1442
|
-
}
|
|
1443
|
-
return
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1753
|
+
});
|
|
1754
|
+
return section3(
|
|
1755
|
+
categoryTitle,
|
|
1756
|
+
metaDescription(category),
|
|
1757
|
+
categoryScore,
|
|
1758
|
+
...categoryMDItems
|
|
1759
|
+
);
|
|
1760
|
+
});
|
|
1761
|
+
return lines3(h22(CATEGORIES_TITLE), ...categoryDetails);
|
|
1762
|
+
}
|
|
1763
|
+
function categoryRef({ title, score, value, displayValue }, pluginTitle) {
|
|
1764
|
+
const auditTitleAsLink = link4(
|
|
1765
|
+
`#${slugify(title)}-${slugify(pluginTitle)}`,
|
|
1766
|
+
title
|
|
1451
1767
|
);
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1768
|
+
const marker = scoreMarker(score, "square");
|
|
1769
|
+
return li2(
|
|
1770
|
+
`${marker}${SPACE}${auditTitleAsLink}${SPACE}(_${pluginTitle}_) - ${boldMd2(
|
|
1771
|
+
(displayValue || value).toString()
|
|
1772
|
+
)}`
|
|
1456
1773
|
);
|
|
1457
1774
|
}
|
|
1458
|
-
function
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
`${getRoundScoreMarker(groupScore)} ${group.title} (_${pluginTitle}_)`
|
|
1775
|
+
function categoryGroupItem({ score = 0, title }, groupAudits, pluginTitle) {
|
|
1776
|
+
const groupTitle = li2(
|
|
1777
|
+
`${scoreMarker(score)}${SPACE}${title}${SPACE}(_${pluginTitle}_)`
|
|
1462
1778
|
);
|
|
1463
|
-
const auditTitles = groupAudits.
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
const auditsData = plugin.audits.reduce((auditAcc, audit) => {
|
|
1479
|
-
const auditTitle = `${audit.title} (${getPluginNameFromSlug(
|
|
1480
|
-
plugin.slug,
|
|
1481
|
-
report.plugins
|
|
1482
|
-
)})`;
|
|
1483
|
-
return auditAcc + h3(auditTitle) + NEW_LINE + NEW_LINE + reportToDetailsSection(audit) + NEW_LINE + NEW_LINE + getDocsAndDescription(audit);
|
|
1484
|
-
}, "");
|
|
1485
|
-
return pluginAcc + auditsData;
|
|
1486
|
-
}, "");
|
|
1487
|
-
return h2("\u{1F6E1}\uFE0F Audits") + NEW_LINE + NEW_LINE + auditsSection;
|
|
1488
|
-
}
|
|
1489
|
-
function reportToDetailsSection(audit) {
|
|
1490
|
-
const detailsTitle = `${getSquaredScoreMarker(audit.score)} ${getAuditResult(
|
|
1491
|
-
audit,
|
|
1492
|
-
true
|
|
1493
|
-
)} (score: ${formatReportScore(audit.score)})`;
|
|
1494
|
-
if (!audit.details?.issues.length) {
|
|
1495
|
-
return detailsTitle;
|
|
1496
|
-
}
|
|
1497
|
-
const detailsTableData = [
|
|
1498
|
-
detailsTableHeaders,
|
|
1499
|
-
...audit.details.issues.map((issue) => {
|
|
1500
|
-
const severity = `${getSeverityIcon(issue.severity)} <i>${issue.severity}</i>`;
|
|
1501
|
-
const message = issue.message;
|
|
1502
|
-
if (!issue.source) {
|
|
1503
|
-
return [severity, message, "", ""];
|
|
1504
|
-
}
|
|
1505
|
-
const file = `<code>${issue.source.file}</code>`;
|
|
1506
|
-
if (!issue.source.position) {
|
|
1507
|
-
return [severity, message, file, ""];
|
|
1508
|
-
}
|
|
1509
|
-
const { startLine, endLine } = issue.source.position;
|
|
1510
|
-
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1511
|
-
return [severity, message, file, line];
|
|
1512
|
-
})
|
|
1513
|
-
];
|
|
1514
|
-
const detailsTable = `<h4>Issues</h4>${tableHtml(detailsTableData)}`;
|
|
1515
|
-
return details(detailsTitle, detailsTable);
|
|
1516
|
-
}
|
|
1517
|
-
function reportToAboutSection(report) {
|
|
1518
|
-
const date = formatDate(/* @__PURE__ */ new Date());
|
|
1519
|
-
const { duration, version: version2, commit, plugins, categories } = report;
|
|
1520
|
-
const commitInfo = commit ? `${commit.message} (${commit.hash})` : "N/A";
|
|
1521
|
-
const reportMetaTable = [
|
|
1522
|
-
reportMetaTableHeaders,
|
|
1523
|
-
[
|
|
1524
|
-
commitInfo,
|
|
1525
|
-
style(version2 || "", ["c"]),
|
|
1526
|
-
formatDuration(duration),
|
|
1527
|
-
plugins.length.toString(),
|
|
1528
|
-
categories.length.toString(),
|
|
1529
|
-
plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
|
|
1530
|
-
]
|
|
1531
|
-
];
|
|
1532
|
-
const pluginMetaTable = [
|
|
1533
|
-
pluginMetaTableHeaders,
|
|
1534
|
-
...plugins.map((plugin) => [
|
|
1535
|
-
plugin.title,
|
|
1536
|
-
plugin.audits.length.toString(),
|
|
1537
|
-
style(plugin.version || "", ["c"]),
|
|
1538
|
-
formatDuration(plugin.duration)
|
|
1539
|
-
])
|
|
1540
|
-
];
|
|
1541
|
-
return (
|
|
1542
|
-
// eslint-disable-next-line prefer-template
|
|
1543
|
-
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"])
|
|
1779
|
+
const auditTitles = groupAudits.map(
|
|
1780
|
+
({ title: auditTitle, score: auditScore, value, displayValue }) => {
|
|
1781
|
+
const auditTitleLink = link4(
|
|
1782
|
+
`#${slugify(auditTitle)}-${slugify(pluginTitle)}`,
|
|
1783
|
+
auditTitle
|
|
1784
|
+
);
|
|
1785
|
+
const marker = scoreMarker(auditScore, "square");
|
|
1786
|
+
return indentation2(
|
|
1787
|
+
li2(
|
|
1788
|
+
`${marker}${SPACE}${auditTitleLink} - ${boldMd2(
|
|
1789
|
+
String(displayValue ?? value)
|
|
1790
|
+
)}`
|
|
1791
|
+
)
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1544
1794
|
);
|
|
1795
|
+
return lines3(groupTitle, ...auditTitles);
|
|
1545
1796
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1797
|
+
|
|
1798
|
+
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
1799
|
+
var { h1: h12, h2: h23, h3: h33, lines: lines4, link: link5, section: section4, code: codeMd } = md;
|
|
1800
|
+
var { bold: boldHtml, details: details2 } = html;
|
|
1801
|
+
function auditDetailsAuditValue({
|
|
1802
|
+
score,
|
|
1803
|
+
value,
|
|
1804
|
+
displayValue
|
|
1549
1805
|
}) {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1806
|
+
return `${scoreMarker(score, "square")} ${boldHtml(
|
|
1807
|
+
String(displayValue ?? value)
|
|
1808
|
+
)} (score: ${formatReportScore(score)})`;
|
|
1809
|
+
}
|
|
1810
|
+
function generateMdReport(report) {
|
|
1811
|
+
const printCategories = report.categories.length > 0;
|
|
1812
|
+
return lines4(
|
|
1813
|
+
h12(reportHeadlineText),
|
|
1814
|
+
printCategories ? categoriesOverviewSection(report) : "",
|
|
1815
|
+
printCategories ? categoriesDetailsSection(report) : "",
|
|
1816
|
+
auditsSection(report),
|
|
1817
|
+
aboutSection(report),
|
|
1818
|
+
`${FOOTER_PREFIX}${SPACE}${link5(README_LINK, "Code PushUp")}`
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
function auditDetailsIssues(issues = []) {
|
|
1822
|
+
if (issues.length === 0) {
|
|
1823
|
+
return "";
|
|
1562
1824
|
}
|
|
1563
|
-
|
|
1825
|
+
const detailsTableData = {
|
|
1826
|
+
title: "Issues",
|
|
1827
|
+
columns: issuesTableHeadings,
|
|
1828
|
+
rows: issues.map(
|
|
1829
|
+
({ severity: severityVal, message, source: sourceVal }) => {
|
|
1830
|
+
const severity = `${severityMarker(severityVal)} <i>${severityVal}</i>`;
|
|
1831
|
+
if (!sourceVal) {
|
|
1832
|
+
return { severity, message, file: "", line: "" };
|
|
1833
|
+
}
|
|
1834
|
+
const file = `<code>${sourceVal.file}</code>`;
|
|
1835
|
+
if (!sourceVal.position) {
|
|
1836
|
+
return { severity, message, file, line: "" };
|
|
1837
|
+
}
|
|
1838
|
+
const { startLine, endLine } = sourceVal.position;
|
|
1839
|
+
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1840
|
+
return { severity, message, file, line };
|
|
1841
|
+
}
|
|
1842
|
+
)
|
|
1843
|
+
};
|
|
1844
|
+
return tableSection(detailsTableData);
|
|
1845
|
+
}
|
|
1846
|
+
function auditDetails(audit) {
|
|
1847
|
+
const { table: table5, issues = [] } = audit.details ?? {};
|
|
1848
|
+
const detailsValue = auditDetailsAuditValue(audit);
|
|
1849
|
+
if (issues.length === 0 && table5 == null) {
|
|
1850
|
+
return section4(detailsValue);
|
|
1851
|
+
}
|
|
1852
|
+
const tableSectionContent = table5 == null ? "" : tableSection(table5);
|
|
1853
|
+
const issuesSectionContent = issues.length > 0 ? auditDetailsIssues(issues) : "";
|
|
1854
|
+
return details2(
|
|
1855
|
+
detailsValue,
|
|
1856
|
+
lines4(tableSectionContent, issuesSectionContent)
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
function auditsSection({
|
|
1860
|
+
plugins
|
|
1861
|
+
}) {
|
|
1862
|
+
const content = plugins.flatMap(
|
|
1863
|
+
({ slug, audits }) => audits.flatMap((audit) => {
|
|
1864
|
+
const auditTitle = `${audit.title}${SPACE}(${getPluginNameFromSlug(
|
|
1865
|
+
slug,
|
|
1866
|
+
plugins
|
|
1867
|
+
)})`;
|
|
1868
|
+
const detailsContent = auditDetails(audit);
|
|
1869
|
+
const descriptionContent = metaDescription(audit);
|
|
1870
|
+
return [h33(auditTitle), detailsContent, descriptionContent];
|
|
1871
|
+
})
|
|
1872
|
+
);
|
|
1873
|
+
return section4(h23("\u{1F6E1}\uFE0F Audits"), ...content);
|
|
1874
|
+
}
|
|
1875
|
+
function aboutSection(report) {
|
|
1876
|
+
const { date, plugins } = report;
|
|
1877
|
+
const reportMetaTable = reportMetaData(report);
|
|
1878
|
+
const pluginMetaTable = reportPluginMeta({ plugins });
|
|
1879
|
+
return lines4(
|
|
1880
|
+
h23("About"),
|
|
1881
|
+
section4(
|
|
1882
|
+
`Report was created by [Code PushUp](${README_LINK}) on ${formatDate(
|
|
1883
|
+
new Date(date)
|
|
1884
|
+
)}.`
|
|
1885
|
+
),
|
|
1886
|
+
tableSection(pluginMetaTable),
|
|
1887
|
+
tableSection(reportMetaTable)
|
|
1888
|
+
);
|
|
1564
1889
|
}
|
|
1565
|
-
function
|
|
1566
|
-
|
|
1567
|
-
|
|
1890
|
+
function reportPluginMeta({ plugins }) {
|
|
1891
|
+
return {
|
|
1892
|
+
columns: [
|
|
1893
|
+
{
|
|
1894
|
+
key: "plugin",
|
|
1895
|
+
align: "left"
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
key: "audits"
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
key: "version"
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
key: "duration"
|
|
1905
|
+
}
|
|
1906
|
+
],
|
|
1907
|
+
rows: plugins.map(
|
|
1908
|
+
({
|
|
1909
|
+
title: pluginTitle,
|
|
1910
|
+
audits,
|
|
1911
|
+
version: pluginVersion,
|
|
1912
|
+
duration: pluginDuration
|
|
1913
|
+
}) => ({
|
|
1914
|
+
plugin: pluginTitle,
|
|
1915
|
+
audits: audits.length.toString(),
|
|
1916
|
+
version: codeMd(pluginVersion || ""),
|
|
1917
|
+
duration: formatDuration(pluginDuration)
|
|
1918
|
+
})
|
|
1919
|
+
)
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function reportMetaData({
|
|
1923
|
+
commit,
|
|
1924
|
+
version: version2,
|
|
1925
|
+
duration,
|
|
1926
|
+
plugins,
|
|
1927
|
+
categories
|
|
1928
|
+
}) {
|
|
1929
|
+
const commitInfo = commit ? `${commit.message}${SPACE}(${commit.hash})` : "N/A";
|
|
1930
|
+
return {
|
|
1931
|
+
columns: [
|
|
1932
|
+
{
|
|
1933
|
+
key: "commit",
|
|
1934
|
+
align: "left"
|
|
1935
|
+
},
|
|
1936
|
+
{
|
|
1937
|
+
key: "version"
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
key: "duration"
|
|
1941
|
+
},
|
|
1942
|
+
{
|
|
1943
|
+
key: "plugins"
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
key: "categories"
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
key: "audits"
|
|
1950
|
+
}
|
|
1951
|
+
],
|
|
1952
|
+
rows: [
|
|
1953
|
+
{
|
|
1954
|
+
commit: commitInfo,
|
|
1955
|
+
version: codeMd(version2 || ""),
|
|
1956
|
+
duration: formatDuration(duration),
|
|
1957
|
+
plugins: plugins.length,
|
|
1958
|
+
categories: categories.length,
|
|
1959
|
+
audits: plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
|
|
1960
|
+
}
|
|
1961
|
+
]
|
|
1962
|
+
};
|
|
1568
1963
|
}
|
|
1569
1964
|
|
|
1570
1965
|
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
1966
|
+
var {
|
|
1967
|
+
h1: h13,
|
|
1968
|
+
h2: h24,
|
|
1969
|
+
lines: lines5,
|
|
1970
|
+
link: link6,
|
|
1971
|
+
bold: boldMd3,
|
|
1972
|
+
italic: italicMd,
|
|
1973
|
+
table: table4,
|
|
1974
|
+
section: section5
|
|
1975
|
+
} = md;
|
|
1976
|
+
var { details: details3 } = html;
|
|
1571
1977
|
var MAX_ROWS = 100;
|
|
1572
1978
|
function generateMdReportsDiff(diff) {
|
|
1573
|
-
return
|
|
1574
|
-
formatDiffHeaderSection(diff),
|
|
1979
|
+
return lines5(
|
|
1980
|
+
section5(formatDiffHeaderSection(diff)),
|
|
1575
1981
|
formatDiffCategoriesSection(diff),
|
|
1576
1982
|
formatDiffGroupsSection(diff),
|
|
1577
1983
|
formatDiffAuditsSection(diff)
|
|
@@ -1579,12 +1985,12 @@ function generateMdReportsDiff(diff) {
|
|
|
1579
1985
|
}
|
|
1580
1986
|
function formatDiffHeaderSection(diff) {
|
|
1581
1987
|
const outcomeTexts = {
|
|
1582
|
-
positive: `\u{1F973} Code PushUp report has ${
|
|
1583
|
-
negative: `\u{1F61F} Code PushUp report has ${
|
|
1584
|
-
mixed: `\u{1F928} Code PushUp report has both ${
|
|
1988
|
+
positive: `\u{1F973} Code PushUp report has ${boldMd3("improved")}`,
|
|
1989
|
+
negative: `\u{1F61F} Code PushUp report has ${boldMd3("regressed")}`,
|
|
1990
|
+
mixed: `\u{1F928} Code PushUp report has both ${boldMd3(
|
|
1585
1991
|
"improvements and regressions"
|
|
1586
1992
|
)}`,
|
|
1587
|
-
unchanged: `\u{1F610} Code PushUp report is ${
|
|
1993
|
+
unchanged: `\u{1F610} Code PushUp report is ${boldMd3("unchanged")}`
|
|
1588
1994
|
};
|
|
1589
1995
|
const outcome = mergeDiffOutcomes(
|
|
1590
1996
|
changesToDiffOutcomes([
|
|
@@ -1594,8 +2000,8 @@ function formatDiffHeaderSection(diff) {
|
|
|
1594
2000
|
])
|
|
1595
2001
|
);
|
|
1596
2002
|
const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
1597
|
-
return
|
|
1598
|
-
|
|
2003
|
+
return lines5(
|
|
2004
|
+
h13("Code PushUp"),
|
|
1599
2005
|
diff.commits ? `${outcomeTexts[outcome]} \u2013 ${styleCommits(diff.commits)}.` : `${outcomeTexts[outcome]}.`
|
|
1600
2006
|
);
|
|
1601
2007
|
}
|
|
@@ -1606,102 +2012,104 @@ function formatDiffCategoriesSection(diff) {
|
|
|
1606
2012
|
if (categoriesCount === 0) {
|
|
1607
2013
|
return "";
|
|
1608
2014
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
formatScoreWithColor(category.scores.
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
category.
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2015
|
+
const columns = [
|
|
2016
|
+
{ key: "category", label: "\u{1F3F7}\uFE0F Category", align: "left" },
|
|
2017
|
+
{ key: "after", label: hasChanges ? "\u2B50 Current score" : "\u2B50 Score" },
|
|
2018
|
+
{ key: "before", label: "\u2B50 Previous score" },
|
|
2019
|
+
{ key: "change", label: "\u{1F504} Score change" }
|
|
2020
|
+
];
|
|
2021
|
+
return lines5(
|
|
2022
|
+
h24("\u{1F3F7}\uFE0F Categories"),
|
|
2023
|
+
categoriesCount > 0 && table4({
|
|
2024
|
+
columns: hasChanges ? columns : columns.slice(0, 2),
|
|
2025
|
+
rows: [
|
|
2026
|
+
...sortChanges(changed).map((category) => ({
|
|
2027
|
+
category: formatTitle(category),
|
|
2028
|
+
after: formatScoreWithColor(category.scores.after),
|
|
2029
|
+
before: formatScoreWithColor(category.scores.before, {
|
|
2030
|
+
skipBold: true
|
|
2031
|
+
}),
|
|
2032
|
+
change: formatScoreChange(category.scores.diff)
|
|
2033
|
+
})),
|
|
2034
|
+
...added.map((category) => ({
|
|
2035
|
+
category: formatTitle(category),
|
|
2036
|
+
after: formatScoreWithColor(category.score),
|
|
2037
|
+
before: italicMd("n/a (\\*)"),
|
|
2038
|
+
change: italicMd("n/a (\\*)")
|
|
2039
|
+
})),
|
|
2040
|
+
...unchanged.map((category) => ({
|
|
2041
|
+
category: formatTitle(category),
|
|
2042
|
+
after: formatScoreWithColor(category.score),
|
|
2043
|
+
before: formatScoreWithColor(category.score, { skipBold: true }),
|
|
2044
|
+
change: "\u2013"
|
|
2045
|
+
}))
|
|
2046
|
+
].map(
|
|
2047
|
+
(row) => hasChanges ? row : { category: row.category, after: row.after }
|
|
2048
|
+
)
|
|
2049
|
+
}),
|
|
2050
|
+
added.length > 0 && section5(italicMd("(\\*) New category."))
|
|
1641
2051
|
);
|
|
1642
2052
|
}
|
|
1643
2053
|
function formatDiffGroupsSection(diff) {
|
|
1644
2054
|
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
1645
2055
|
return "";
|
|
1646
2056
|
}
|
|
1647
|
-
return
|
|
1648
|
-
|
|
2057
|
+
return lines5(
|
|
2058
|
+
h24("\u{1F5C3}\uFE0F Groups"),
|
|
1649
2059
|
formatGroupsOrAuditsDetails("group", diff.groups, {
|
|
1650
|
-
|
|
1651
|
-
"\u{1F50C} Plugin",
|
|
1652
|
-
"\u{1F5C3}\uFE0F Group",
|
|
1653
|
-
"\u2B50 Current score",
|
|
1654
|
-
"\u2B50 Previous score",
|
|
1655
|
-
"\u{1F504} Score change"
|
|
2060
|
+
columns: [
|
|
2061
|
+
{ key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
|
|
2062
|
+
{ key: "group", label: "\u{1F5C3}\uFE0F Group", align: "left" },
|
|
2063
|
+
{ key: "after", label: "\u2B50 Current score" },
|
|
2064
|
+
{ key: "before", label: "\u2B50 Previous score" },
|
|
2065
|
+
{ key: "change", label: "\u{1F504} Score change" }
|
|
1656
2066
|
],
|
|
1657
|
-
rows: sortChanges(diff.groups.changed).map((group) =>
|
|
1658
|
-
group.plugin
|
|
1659
|
-
group
|
|
1660
|
-
formatScoreWithColor(group.scores.after),
|
|
1661
|
-
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
1662
|
-
formatScoreChange(group.scores.diff)
|
|
1663
|
-
|
|
1664
|
-
align: ["l", "l", "c", "c", "c"]
|
|
2067
|
+
rows: sortChanges(diff.groups.changed).map((group) => ({
|
|
2068
|
+
plugin: formatTitle(group.plugin),
|
|
2069
|
+
group: formatTitle(group),
|
|
2070
|
+
after: formatScoreWithColor(group.scores.after),
|
|
2071
|
+
before: formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
2072
|
+
change: formatScoreChange(group.scores.diff)
|
|
2073
|
+
}))
|
|
1665
2074
|
})
|
|
1666
2075
|
);
|
|
1667
2076
|
}
|
|
1668
2077
|
function formatDiffAuditsSection(diff) {
|
|
1669
|
-
return
|
|
1670
|
-
|
|
2078
|
+
return lines5(
|
|
2079
|
+
h24("\u{1F6E1}\uFE0F Audits"),
|
|
1671
2080
|
formatGroupsOrAuditsDetails("audit", diff.audits, {
|
|
1672
|
-
|
|
1673
|
-
"\u{1F50C} Plugin",
|
|
1674
|
-
"\u{1F6E1}\uFE0F Audit",
|
|
1675
|
-
"\u{1F4CF} Current value",
|
|
1676
|
-
"\u{1F4CF} Previous value",
|
|
1677
|
-
"\u{1F504} Value change"
|
|
2081
|
+
columns: [
|
|
2082
|
+
{ key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
|
|
2083
|
+
{ key: "audit", label: "\u{1F6E1}\uFE0F Audit", align: "left" },
|
|
2084
|
+
{ key: "after", label: "\u{1F4CF} Current value" },
|
|
2085
|
+
{ key: "before", label: "\u{1F4CF} Previous value" },
|
|
2086
|
+
{ key: "change", label: "\u{1F504} Value change" }
|
|
1678
2087
|
],
|
|
1679
|
-
rows: sortChanges(diff.audits.changed).map((audit) =>
|
|
1680
|
-
audit.plugin
|
|
1681
|
-
audit
|
|
1682
|
-
`${
|
|
2088
|
+
rows: sortChanges(diff.audits.changed).map((audit) => ({
|
|
2089
|
+
plugin: formatTitle(audit.plugin),
|
|
2090
|
+
audit: formatTitle(audit),
|
|
2091
|
+
after: `${scoreMarker(audit.scores.after, "square")} ${boldMd3(
|
|
1683
2092
|
audit.displayValues.after || audit.values.after.toString()
|
|
1684
2093
|
)}`,
|
|
1685
|
-
`${
|
|
1686
|
-
formatValueChange(audit)
|
|
1687
|
-
|
|
1688
|
-
align: ["l", "l", "c", "c", "c"]
|
|
2094
|
+
before: `${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
2095
|
+
change: formatValueChange(audit)
|
|
2096
|
+
}))
|
|
1689
2097
|
})
|
|
1690
2098
|
);
|
|
1691
2099
|
}
|
|
1692
|
-
function formatGroupsOrAuditsDetails(token, { changed, unchanged },
|
|
1693
|
-
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) :
|
|
2100
|
+
function formatGroupsOrAuditsDetails(token, { changed, unchanged }, tableData) {
|
|
2101
|
+
return changed.length === 0 ? summarizeUnchanged(token, { changed, unchanged }) : details3(
|
|
1694
2102
|
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
2103
|
+
lines5(
|
|
2104
|
+
table4({
|
|
2105
|
+
...tableData,
|
|
2106
|
+
rows: tableData.rows.slice(0, MAX_ROWS)
|
|
2107
|
+
// use never to avoid typing problem
|
|
2108
|
+
}),
|
|
2109
|
+
changed.length > MAX_ROWS && italicMd(
|
|
1701
2110
|
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
1702
2111
|
token
|
|
1703
|
-
)} are listed above for brevity
|
|
1704
|
-
["i"]
|
|
2112
|
+
)} are listed above for brevity.`
|
|
1705
2113
|
),
|
|
1706
2114
|
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
1707
2115
|
)
|
|
@@ -1722,11 +2130,13 @@ function formatValueChange({
|
|
|
1722
2130
|
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
1723
2131
|
}
|
|
1724
2132
|
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
1725
|
-
return
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
2133
|
+
return section5(
|
|
2134
|
+
[
|
|
2135
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
2136
|
+
unchanged.length === 1 ? "is" : "are",
|
|
2137
|
+
"unchanged."
|
|
2138
|
+
].join(" ")
|
|
2139
|
+
);
|
|
1730
2140
|
}
|
|
1731
2141
|
function summarizeDiffOutcomes(outcomes, token) {
|
|
1732
2142
|
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
@@ -1746,6 +2156,15 @@ function summarizeDiffOutcomes(outcomes, token) {
|
|
|
1746
2156
|
}
|
|
1747
2157
|
}).join(", ");
|
|
1748
2158
|
}
|
|
2159
|
+
function formatTitle({
|
|
2160
|
+
title,
|
|
2161
|
+
docsUrl
|
|
2162
|
+
}) {
|
|
2163
|
+
if (docsUrl) {
|
|
2164
|
+
return link6(docsUrl, title);
|
|
2165
|
+
}
|
|
2166
|
+
return title;
|
|
2167
|
+
}
|
|
1749
2168
|
function sortChanges(changes) {
|
|
1750
2169
|
return [...changes].sort(
|
|
1751
2170
|
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
@@ -1793,7 +2212,7 @@ function log(msg = "") {
|
|
|
1793
2212
|
}
|
|
1794
2213
|
function logStdoutSummary(report) {
|
|
1795
2214
|
const printCategories = report.categories.length > 0;
|
|
1796
|
-
log(
|
|
2215
|
+
log(reportToHeaderSection(report));
|
|
1797
2216
|
log();
|
|
1798
2217
|
logPlugins(report);
|
|
1799
2218
|
if (printCategories) {
|
|
@@ -1802,7 +2221,7 @@ function logStdoutSummary(report) {
|
|
|
1802
2221
|
log(`${FOOTER_PREFIX} ${CODE_PUSHUP_DOMAIN}`);
|
|
1803
2222
|
log();
|
|
1804
2223
|
}
|
|
1805
|
-
function
|
|
2224
|
+
function reportToHeaderSection(report) {
|
|
1806
2225
|
const { packageName, version: version2 } = report;
|
|
1807
2226
|
return `${chalk4.bold(reportHeadlineText)} - ${packageName}@${version2}`;
|
|
1808
2227
|
}
|
|
@@ -1842,16 +2261,16 @@ function logCategories({ categories, plugins }) {
|
|
|
1842
2261
|
applyScoreColor({ score }),
|
|
1843
2262
|
countCategoryAudits(refs, plugins)
|
|
1844
2263
|
]);
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
|
|
2264
|
+
const table5 = ui().table();
|
|
2265
|
+
table5.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
2266
|
+
table5.head(
|
|
1848
2267
|
reportRawOverviewTableHeaders.map((heading, idx) => ({
|
|
1849
2268
|
content: chalk4.cyan(heading),
|
|
1850
2269
|
hAlign: hAlign(idx)
|
|
1851
2270
|
}))
|
|
1852
2271
|
);
|
|
1853
2272
|
rows.forEach(
|
|
1854
|
-
(row) =>
|
|
2273
|
+
(row) => table5.row(
|
|
1855
2274
|
row.map((content, idx) => ({
|
|
1856
2275
|
content: content.toString(),
|
|
1857
2276
|
hAlign: hAlign(idx)
|
|
@@ -1860,19 +2279,19 @@ function logCategories({ categories, plugins }) {
|
|
|
1860
2279
|
);
|
|
1861
2280
|
log(chalk4.magentaBright.bold("Categories"));
|
|
1862
2281
|
log();
|
|
1863
|
-
|
|
2282
|
+
table5.render();
|
|
1864
2283
|
log();
|
|
1865
2284
|
}
|
|
1866
2285
|
function applyScoreColor({ score, text }) {
|
|
1867
2286
|
const formattedScore = text ?? formatReportScore(score);
|
|
1868
|
-
const
|
|
2287
|
+
const style = text ? chalk4 : chalk4.bold;
|
|
1869
2288
|
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1870
|
-
return
|
|
2289
|
+
return style.green(formattedScore);
|
|
1871
2290
|
}
|
|
1872
2291
|
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1873
|
-
return
|
|
2292
|
+
return style.yellow(formattedScore);
|
|
1874
2293
|
}
|
|
1875
|
-
return
|
|
2294
|
+
return style.red(formattedScore);
|
|
1876
2295
|
}
|
|
1877
2296
|
|
|
1878
2297
|
// packages/utils/src/lib/reports/scoring.ts
|
|
@@ -2034,12 +2453,22 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2034
2453
|
|
|
2035
2454
|
// packages/core/package.json
|
|
2036
2455
|
var name = "@code-pushup/core";
|
|
2037
|
-
var version = "0.
|
|
2456
|
+
var version = "0.42.0";
|
|
2038
2457
|
|
|
2039
2458
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2040
2459
|
import chalk5 from "chalk";
|
|
2041
2460
|
|
|
2042
2461
|
// packages/core/src/lib/normalize.ts
|
|
2462
|
+
function normalizeIssue(issue, gitRoot) {
|
|
2463
|
+
const { source, ...issueWithoutSource } = issue;
|
|
2464
|
+
return source == null ? issue : {
|
|
2465
|
+
...issueWithoutSource,
|
|
2466
|
+
source: {
|
|
2467
|
+
...source,
|
|
2468
|
+
file: formatGitPath(source.file, gitRoot)
|
|
2469
|
+
}
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2043
2472
|
async function normalizeAuditOutputs(audits) {
|
|
2044
2473
|
const gitRoot = await getGitRoot();
|
|
2045
2474
|
return audits.map((audit) => {
|
|
@@ -2051,13 +2480,7 @@ async function normalizeAuditOutputs(audits) {
|
|
|
2051
2480
|
details: {
|
|
2052
2481
|
...audit.details,
|
|
2053
2482
|
issues: audit.details.issues.map(
|
|
2054
|
-
(issue) => issue
|
|
2055
|
-
...issue,
|
|
2056
|
-
source: {
|
|
2057
|
-
...issue.source,
|
|
2058
|
-
file: formatGitPath(issue.source.file, gitRoot)
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2483
|
+
(issue) => normalizeIssue(issue, gitRoot)
|
|
2061
2484
|
)
|
|
2062
2485
|
}
|
|
2063
2486
|
};
|
|
@@ -2095,7 +2518,11 @@ async function executeRunnerFunction(runner, onProgress) {
|
|
|
2095
2518
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2096
2519
|
var PluginOutputMissingAuditError = class extends Error {
|
|
2097
2520
|
constructor(auditSlug) {
|
|
2098
|
-
super(
|
|
2521
|
+
super(
|
|
2522
|
+
`Audit metadata not present in plugin config. Missing slug: ${chalk5.bold(
|
|
2523
|
+
auditSlug
|
|
2524
|
+
)}`
|
|
2525
|
+
);
|
|
2099
2526
|
}
|
|
2100
2527
|
};
|
|
2101
2528
|
async function executePlugin(pluginConfig, onProgress) {
|
|
@@ -2109,7 +2536,11 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
2109
2536
|
} = pluginConfig;
|
|
2110
2537
|
const runnerResult = typeof runner === "object" ? await executeRunnerConfig(runner, onProgress) : await executeRunnerFunction(runner, onProgress);
|
|
2111
2538
|
const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult;
|
|
2112
|
-
const
|
|
2539
|
+
const result = auditOutputsSchema.safeParse(unvalidatedAuditOutputs);
|
|
2540
|
+
if (!result.success) {
|
|
2541
|
+
throw new Error(`Audit output is invalid: ${result.error.message}`);
|
|
2542
|
+
}
|
|
2543
|
+
const auditOutputs = result.data;
|
|
2113
2544
|
auditOutputsCorrelateWithPluginOutput(auditOutputs, pluginConfigAudits);
|
|
2114
2545
|
const normalizedAuditOutputs = await normalizeAuditOutputs(auditOutputs);
|
|
2115
2546
|
const auditReports = normalizedAuditOutputs.map(
|
|
@@ -2129,32 +2560,48 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
2129
2560
|
...groups && { groups }
|
|
2130
2561
|
};
|
|
2131
2562
|
}
|
|
2563
|
+
var wrapProgress = async (pluginCfg, steps, progressBar) => {
|
|
2564
|
+
progressBar?.updateTitle(`Executing ${chalk5.bold(pluginCfg.title)}`);
|
|
2565
|
+
try {
|
|
2566
|
+
const pluginReport = await executePlugin(pluginCfg);
|
|
2567
|
+
progressBar?.incrementInSteps(steps);
|
|
2568
|
+
return pluginReport;
|
|
2569
|
+
} catch (error) {
|
|
2570
|
+
progressBar?.incrementInSteps(steps);
|
|
2571
|
+
throw new Error(
|
|
2572
|
+
error instanceof Error ? `- Plugin ${chalk5.bold(pluginCfg.title)} (${chalk5.bold(
|
|
2573
|
+
pluginCfg.slug
|
|
2574
|
+
)}) produced the following error:
|
|
2575
|
+
- ${error.message}` : String(error)
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2132
2579
|
async function executePlugins(plugins, options) {
|
|
2133
2580
|
const { progress = false } = options ?? {};
|
|
2134
2581
|
const progressBar = progress ? getProgressBar("Run plugins") : null;
|
|
2135
|
-
const pluginsResult = await plugins.reduce(
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
progressBar?.incrementInSteps(plugins.length);
|
|
2143
|
-
return [
|
|
2144
|
-
...await acc,
|
|
2145
|
-
Promise.reject(error instanceof Error ? error.message : String(error))
|
|
2146
|
-
];
|
|
2147
|
-
}
|
|
2148
|
-
}, Promise.resolve([]));
|
|
2582
|
+
const pluginsResult = await plugins.reduce(
|
|
2583
|
+
async (acc, pluginCfg) => [
|
|
2584
|
+
...await acc,
|
|
2585
|
+
wrapProgress(pluginCfg, plugins.length, progressBar)
|
|
2586
|
+
],
|
|
2587
|
+
Promise.resolve([])
|
|
2588
|
+
);
|
|
2149
2589
|
progressBar?.endProgress("Done running plugins");
|
|
2150
2590
|
const errorsTransform = ({ reason }) => String(reason);
|
|
2151
2591
|
const results = await Promise.allSettled(pluginsResult);
|
|
2152
2592
|
logMultipleResults(results, "Plugins", void 0, errorsTransform);
|
|
2153
2593
|
const { fulfilled, rejected } = groupByStatus(results);
|
|
2154
2594
|
if (rejected.length > 0) {
|
|
2155
|
-
const errorMessages = rejected.map(({ reason }) => String(reason)).join("
|
|
2595
|
+
const errorMessages = rejected.map(({ reason }) => String(reason)).join("\n");
|
|
2156
2596
|
throw new Error(
|
|
2157
|
-
`
|
|
2597
|
+
`Executing ${pluralizeToken(
|
|
2598
|
+
"plugin",
|
|
2599
|
+
rejected.length
|
|
2600
|
+
)} failed.
|
|
2601
|
+
|
|
2602
|
+
${errorMessages}
|
|
2603
|
+
|
|
2604
|
+
`
|
|
2158
2605
|
);
|
|
2159
2606
|
}
|
|
2160
2607
|
return fulfilled.map((result) => result.value);
|
|
@@ -2317,8 +2764,7 @@ function compareAudits2(reports) {
|
|
|
2317
2764
|
}
|
|
2318
2765
|
function categoryToResult(category) {
|
|
2319
2766
|
return {
|
|
2320
|
-
|
|
2321
|
-
title: category.title,
|
|
2767
|
+
...selectMeta(category),
|
|
2322
2768
|
score: category.score
|
|
2323
2769
|
};
|
|
2324
2770
|
}
|
|
@@ -2327,8 +2773,7 @@ function categoryPairToDiff({
|
|
|
2327
2773
|
after
|
|
2328
2774
|
}) {
|
|
2329
2775
|
return {
|
|
2330
|
-
|
|
2331
|
-
title: after.title,
|
|
2776
|
+
...selectMeta(after),
|
|
2332
2777
|
scores: {
|
|
2333
2778
|
before: before.score,
|
|
2334
2779
|
after: after.score,
|
|
@@ -2338,12 +2783,8 @@ function categoryPairToDiff({
|
|
|
2338
2783
|
}
|
|
2339
2784
|
function pluginGroupToResult({ group, plugin }) {
|
|
2340
2785
|
return {
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
plugin: {
|
|
2344
|
-
slug: plugin.slug,
|
|
2345
|
-
title: plugin.title
|
|
2346
|
-
},
|
|
2786
|
+
...selectMeta(group),
|
|
2787
|
+
plugin: selectMeta(plugin),
|
|
2347
2788
|
score: group.score
|
|
2348
2789
|
};
|
|
2349
2790
|
}
|
|
@@ -2352,12 +2793,8 @@ function pluginGroupPairToDiff({
|
|
|
2352
2793
|
after
|
|
2353
2794
|
}) {
|
|
2354
2795
|
return {
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
plugin: {
|
|
2358
|
-
slug: after.plugin.slug,
|
|
2359
|
-
title: after.plugin.title
|
|
2360
|
-
},
|
|
2796
|
+
...selectMeta(after.group),
|
|
2797
|
+
plugin: selectMeta(after.plugin),
|
|
2361
2798
|
scores: {
|
|
2362
2799
|
before: before.group.score,
|
|
2363
2800
|
after: after.group.score,
|
|
@@ -2367,12 +2804,8 @@ function pluginGroupPairToDiff({
|
|
|
2367
2804
|
}
|
|
2368
2805
|
function pluginAuditToResult({ audit, plugin }) {
|
|
2369
2806
|
return {
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
plugin: {
|
|
2373
|
-
slug: plugin.slug,
|
|
2374
|
-
title: plugin.title
|
|
2375
|
-
},
|
|
2807
|
+
...selectMeta(audit),
|
|
2808
|
+
plugin: selectMeta(plugin),
|
|
2376
2809
|
score: audit.score,
|
|
2377
2810
|
value: audit.value,
|
|
2378
2811
|
displayValue: audit.displayValue
|
|
@@ -2383,12 +2816,8 @@ function pluginAuditPairToDiff({
|
|
|
2383
2816
|
after
|
|
2384
2817
|
}) {
|
|
2385
2818
|
return {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
plugin: {
|
|
2389
|
-
slug: after.plugin.slug,
|
|
2390
|
-
title: after.plugin.title
|
|
2391
|
-
},
|
|
2819
|
+
...selectMeta(after.audit),
|
|
2820
|
+
plugin: selectMeta(after.plugin),
|
|
2392
2821
|
scores: {
|
|
2393
2822
|
before: before.audit.score,
|
|
2394
2823
|
after: after.audit.score,
|
|
@@ -2405,6 +2834,15 @@ function pluginAuditPairToDiff({
|
|
|
2405
2834
|
}
|
|
2406
2835
|
};
|
|
2407
2836
|
}
|
|
2837
|
+
function selectMeta(meta) {
|
|
2838
|
+
return {
|
|
2839
|
+
slug: meta.slug,
|
|
2840
|
+
title: meta.title,
|
|
2841
|
+
...meta.docsUrl && {
|
|
2842
|
+
docsUrl: meta.docsUrl
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2408
2846
|
|
|
2409
2847
|
// packages/core/src/lib/compare.ts
|
|
2410
2848
|
async function compareReportFiles(inputPaths, persistConfig) {
|
|
@@ -2460,46 +2898,6 @@ function reportsDiffToFileContent(reportsDiff, format) {
|
|
|
2460
2898
|
}
|
|
2461
2899
|
}
|
|
2462
2900
|
|
|
2463
|
-
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
2464
|
-
import { join as join6 } from "node:path";
|
|
2465
|
-
var ConfigPathError = class extends Error {
|
|
2466
|
-
constructor(configPath) {
|
|
2467
|
-
super(`Provided path '${configPath}' is not valid.`);
|
|
2468
|
-
}
|
|
2469
|
-
};
|
|
2470
|
-
async function readRcByPath(filepath, tsconfig) {
|
|
2471
|
-
if (filepath.length === 0) {
|
|
2472
|
-
throw new Error("The path to the configuration file is empty.");
|
|
2473
|
-
}
|
|
2474
|
-
if (!await fileExists(filepath)) {
|
|
2475
|
-
throw new ConfigPathError(filepath);
|
|
2476
|
-
}
|
|
2477
|
-
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
2478
|
-
return coreConfigSchema.parse(cfg);
|
|
2479
|
-
}
|
|
2480
|
-
async function autoloadRc(tsconfig) {
|
|
2481
|
-
let ext = "";
|
|
2482
|
-
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
2483
|
-
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
2484
|
-
const exists2 = await fileExists(path);
|
|
2485
|
-
if (exists2) {
|
|
2486
|
-
ext = extension;
|
|
2487
|
-
break;
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
if (!ext) {
|
|
2491
|
-
throw new Error(
|
|
2492
|
-
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
2493
|
-
"|"
|
|
2494
|
-
)}) present in ${process.cwd()}`
|
|
2495
|
-
);
|
|
2496
|
-
}
|
|
2497
|
-
return readRcByPath(
|
|
2498
|
-
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
2499
|
-
tsconfig
|
|
2500
|
-
);
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
2901
|
// packages/core/src/lib/upload.ts
|
|
2504
2902
|
import {
|
|
2505
2903
|
uploadToPortal
|
|
@@ -2545,17 +2943,33 @@ function groupToGQL(group) {
|
|
|
2545
2943
|
};
|
|
2546
2944
|
}
|
|
2547
2945
|
function auditToGQL(audit) {
|
|
2946
|
+
const {
|
|
2947
|
+
slug,
|
|
2948
|
+
title,
|
|
2949
|
+
description,
|
|
2950
|
+
docsUrl,
|
|
2951
|
+
score,
|
|
2952
|
+
value,
|
|
2953
|
+
displayValue: formattedValue,
|
|
2954
|
+
details: details4
|
|
2955
|
+
} = audit;
|
|
2956
|
+
const {
|
|
2957
|
+
issues
|
|
2958
|
+
/*, table */
|
|
2959
|
+
} = details4 ?? {};
|
|
2548
2960
|
return {
|
|
2549
|
-
slug
|
|
2550
|
-
title
|
|
2551
|
-
description
|
|
2552
|
-
docsUrl
|
|
2553
|
-
score
|
|
2554
|
-
value
|
|
2555
|
-
formattedValue
|
|
2556
|
-
...
|
|
2961
|
+
slug,
|
|
2962
|
+
title,
|
|
2963
|
+
description,
|
|
2964
|
+
docsUrl,
|
|
2965
|
+
score,
|
|
2966
|
+
value,
|
|
2967
|
+
formattedValue,
|
|
2968
|
+
...details4 && {
|
|
2557
2969
|
details: {
|
|
2558
|
-
issues:
|
|
2970
|
+
...issues && { issues: issues.map(issueToGQL) }
|
|
2971
|
+
// @TODO add when https://github.com/code-pushup/cli/issues/530 is implemented
|
|
2972
|
+
// ...(table ? {table} : {}),
|
|
2559
2973
|
}
|
|
2560
2974
|
}
|
|
2561
2975
|
};
|
|
@@ -2579,6 +2993,7 @@ function categoryToGQL(category) {
|
|
|
2579
2993
|
slug: category.slug,
|
|
2580
2994
|
title: category.title,
|
|
2581
2995
|
description: category.description,
|
|
2996
|
+
isBinary: category.isBinary,
|
|
2582
2997
|
refs: category.refs.map((ref) => ({
|
|
2583
2998
|
plugin: ref.plugin,
|
|
2584
2999
|
type: categoryRefTypeToGQL(ref.type),
|
|
@@ -2627,6 +3042,80 @@ async function upload(options, uploadFn = uploadToPortal) {
|
|
|
2627
3042
|
};
|
|
2628
3043
|
return uploadFn({ apiKey, server, data, timeout });
|
|
2629
3044
|
}
|
|
3045
|
+
|
|
3046
|
+
// packages/core/src/lib/history.ts
|
|
3047
|
+
async function history(config, commits) {
|
|
3048
|
+
const initialBranch = await getCurrentBranchOrTag();
|
|
3049
|
+
const { skipUploads = false, forceCleanStatus, persist } = config;
|
|
3050
|
+
const reports = [];
|
|
3051
|
+
for (const commit of commits) {
|
|
3052
|
+
ui().logger.info(`Collect ${commit}`);
|
|
3053
|
+
await safeCheckout(commit, forceCleanStatus);
|
|
3054
|
+
const currentConfig = {
|
|
3055
|
+
...config,
|
|
3056
|
+
persist: {
|
|
3057
|
+
...persist,
|
|
3058
|
+
format: ["json"],
|
|
3059
|
+
filename: `${commit}-report`
|
|
3060
|
+
}
|
|
3061
|
+
};
|
|
3062
|
+
await collectAndPersistReports(currentConfig);
|
|
3063
|
+
if (skipUploads) {
|
|
3064
|
+
ui().logger.info("Upload is skipped because skipUploads is set to true.");
|
|
3065
|
+
} else {
|
|
3066
|
+
if (currentConfig.upload) {
|
|
3067
|
+
await upload(currentConfig);
|
|
3068
|
+
} else {
|
|
3069
|
+
ui().logger.info(
|
|
3070
|
+
"Upload is skipped because upload config is undefined."
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
reports.push(currentConfig.persist.filename);
|
|
3075
|
+
}
|
|
3076
|
+
await safeCheckout(initialBranch, forceCleanStatus);
|
|
3077
|
+
return reports;
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
// packages/core/src/lib/implementation/read-rc-file.ts
|
|
3081
|
+
import { join as join6 } from "node:path";
|
|
3082
|
+
var ConfigPathError = class extends Error {
|
|
3083
|
+
constructor(configPath) {
|
|
3084
|
+
super(`Provided path '${configPath}' is not valid.`);
|
|
3085
|
+
}
|
|
3086
|
+
};
|
|
3087
|
+
async function readRcByPath(filepath, tsconfig) {
|
|
3088
|
+
if (filepath.length === 0) {
|
|
3089
|
+
throw new Error("The path to the configuration file is empty.");
|
|
3090
|
+
}
|
|
3091
|
+
if (!await fileExists(filepath)) {
|
|
3092
|
+
throw new ConfigPathError(filepath);
|
|
3093
|
+
}
|
|
3094
|
+
const cfg = await importEsmModule({ filepath, tsconfig });
|
|
3095
|
+
return coreConfigSchema.parse(cfg);
|
|
3096
|
+
}
|
|
3097
|
+
async function autoloadRc(tsconfig) {
|
|
3098
|
+
let ext = "";
|
|
3099
|
+
for (const extension of SUPPORTED_CONFIG_FILE_FORMATS) {
|
|
3100
|
+
const path = `${CONFIG_FILE_NAME}.${extension}`;
|
|
3101
|
+
const exists2 = await fileExists(path);
|
|
3102
|
+
if (exists2) {
|
|
3103
|
+
ext = extension;
|
|
3104
|
+
break;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
if (!ext) {
|
|
3108
|
+
throw new Error(
|
|
3109
|
+
`No file ${CONFIG_FILE_NAME}.(${SUPPORTED_CONFIG_FILE_FORMATS.join(
|
|
3110
|
+
"|"
|
|
3111
|
+
)}) present in ${process.cwd()}`
|
|
3112
|
+
);
|
|
3113
|
+
}
|
|
3114
|
+
return readRcByPath(
|
|
3115
|
+
join6(process.cwd(), `${CONFIG_FILE_NAME}.${ext}`),
|
|
3116
|
+
tsconfig
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
2630
3119
|
export {
|
|
2631
3120
|
ConfigPathError,
|
|
2632
3121
|
PersistDirError,
|
|
@@ -2639,6 +3128,7 @@ export {
|
|
|
2639
3128
|
compareReports,
|
|
2640
3129
|
executePlugin,
|
|
2641
3130
|
executePlugins,
|
|
3131
|
+
history,
|
|
2642
3132
|
persistReport,
|
|
2643
3133
|
readRcByPath,
|
|
2644
3134
|
upload
|