@code-pushup/utils 0.49.0 → 0.51.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 +450 -265
- package/package.json +5 -31
- package/src/index.d.ts +13 -12
- package/src/lib/errors.d.ts +1 -0
- package/src/lib/execute-process.d.ts +5 -3
- package/src/lib/git/git.commits-and-tags.d.ts +2 -2
- package/src/lib/git/git.d.ts +1 -1
- package/src/lib/merge-configs.d.ts +1 -1
- package/src/lib/progress.d.ts +1 -1
- package/src/lib/reports/constants.d.ts +1 -0
- package/src/lib/reports/flatten-plugins.d.ts +1 -1
- package/src/lib/reports/formatting.d.ts +15 -2
- package/src/lib/reports/generate-md-report-categoy-section.d.ts +3 -3
- package/src/lib/reports/generate-md-report.d.ts +7 -7
- package/src/lib/reports/generate-md-reports-diff-utils.d.ts +29 -0
- package/src/lib/reports/generate-md-reports-diff.d.ts +5 -2
- package/src/lib/reports/ide-environment.d.ts +3 -0
- package/src/lib/reports/load-report.d.ts +1 -1
- package/src/lib/reports/log-stdout-summary.d.ts +1 -1
- package/src/lib/reports/scoring.d.ts +2 -2
- package/src/lib/reports/sorting.d.ts +2 -2
- package/src/lib/reports/types.d.ts +3 -1
- package/src/lib/reports/utils.d.ts +5 -4
- package/src/lib/text-formats/html/table.d.ts +1 -1
- package/src/lib/text-formats/table.d.ts +1 -1
- package/src/lib/types.d.ts +9 -0
package/index.js
CHANGED
|
@@ -155,9 +155,27 @@ function hasNonZeroWeightedRef(refs) {
|
|
|
155
155
|
return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// packages/models/src/lib/
|
|
158
|
+
// packages/models/src/lib/source.ts
|
|
159
159
|
import { z as z2 } from "zod";
|
|
160
|
-
var
|
|
160
|
+
var sourceFileLocationSchema = z2.object(
|
|
161
|
+
{
|
|
162
|
+
file: filePathSchema.describe("Relative path to source file in Git repo"),
|
|
163
|
+
position: z2.object(
|
|
164
|
+
{
|
|
165
|
+
startLine: positiveIntSchema.describe("Start line"),
|
|
166
|
+
startColumn: positiveIntSchema.describe("Start column").optional(),
|
|
167
|
+
endLine: positiveIntSchema.describe("End line").optional(),
|
|
168
|
+
endColumn: positiveIntSchema.describe("End column").optional()
|
|
169
|
+
},
|
|
170
|
+
{ description: "Location in file" }
|
|
171
|
+
).optional()
|
|
172
|
+
},
|
|
173
|
+
{ description: "Source file location" }
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// packages/models/src/lib/audit.ts
|
|
177
|
+
import { z as z3 } from "zod";
|
|
178
|
+
var auditSchema = z3.object({
|
|
161
179
|
slug: slugSchema.describe("ID (unique within plugin)")
|
|
162
180
|
}).merge(
|
|
163
181
|
metaSchema({
|
|
@@ -167,7 +185,7 @@ var auditSchema = z2.object({
|
|
|
167
185
|
description: "List of scorable metrics for the given plugin"
|
|
168
186
|
})
|
|
169
187
|
);
|
|
170
|
-
var pluginAuditsSchema =
|
|
188
|
+
var pluginAuditsSchema = z3.array(auditSchema, {
|
|
171
189
|
description: "List of audits maintained in a plugin"
|
|
172
190
|
}).min(1).refine(
|
|
173
191
|
(auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
|
|
@@ -186,31 +204,16 @@ function getDuplicateSlugsInAudits(audits) {
|
|
|
186
204
|
}
|
|
187
205
|
|
|
188
206
|
// packages/models/src/lib/audit-output.ts
|
|
189
|
-
import { z as
|
|
207
|
+
import { z as z6 } from "zod";
|
|
190
208
|
|
|
191
209
|
// packages/models/src/lib/issue.ts
|
|
192
|
-
import { z as
|
|
193
|
-
var
|
|
194
|
-
{
|
|
195
|
-
file: filePathSchema.describe("Relative path to source file in Git repo"),
|
|
196
|
-
position: z3.object(
|
|
197
|
-
{
|
|
198
|
-
startLine: positiveIntSchema.describe("Start line"),
|
|
199
|
-
startColumn: positiveIntSchema.describe("Start column").optional(),
|
|
200
|
-
endLine: positiveIntSchema.describe("End line").optional(),
|
|
201
|
-
endColumn: positiveIntSchema.describe("End column").optional()
|
|
202
|
-
},
|
|
203
|
-
{ description: "Location in file" }
|
|
204
|
-
).optional()
|
|
205
|
-
},
|
|
206
|
-
{ description: "Source file location" }
|
|
207
|
-
);
|
|
208
|
-
var issueSeveritySchema = z3.enum(["info", "warning", "error"], {
|
|
210
|
+
import { z as z4 } from "zod";
|
|
211
|
+
var issueSeveritySchema = z4.enum(["info", "warning", "error"], {
|
|
209
212
|
description: "Severity level"
|
|
210
213
|
});
|
|
211
|
-
var issueSchema =
|
|
214
|
+
var issueSchema = z4.object(
|
|
212
215
|
{
|
|
213
|
-
message:
|
|
216
|
+
message: z4.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
|
|
214
217
|
severity: issueSeveritySchema,
|
|
215
218
|
source: sourceFileLocationSchema.optional()
|
|
216
219
|
},
|
|
@@ -218,60 +221,60 @@ var issueSchema = z3.object(
|
|
|
218
221
|
);
|
|
219
222
|
|
|
220
223
|
// packages/models/src/lib/table.ts
|
|
221
|
-
import { z as
|
|
222
|
-
var tableAlignmentSchema =
|
|
224
|
+
import { z as z5 } from "zod";
|
|
225
|
+
var tableAlignmentSchema = z5.enum(["left", "center", "right"], {
|
|
223
226
|
description: "Cell alignment"
|
|
224
227
|
});
|
|
225
|
-
var tableColumnObjectSchema =
|
|
226
|
-
key:
|
|
227
|
-
label:
|
|
228
|
+
var tableColumnObjectSchema = z5.object({
|
|
229
|
+
key: z5.string(),
|
|
230
|
+
label: z5.string().optional(),
|
|
228
231
|
align: tableAlignmentSchema.optional()
|
|
229
232
|
});
|
|
230
|
-
var tableRowObjectSchema =
|
|
233
|
+
var tableRowObjectSchema = z5.record(tableCellValueSchema, {
|
|
231
234
|
description: "Object row"
|
|
232
235
|
});
|
|
233
|
-
var tableRowPrimitiveSchema =
|
|
236
|
+
var tableRowPrimitiveSchema = z5.array(tableCellValueSchema, {
|
|
234
237
|
description: "Primitive row"
|
|
235
238
|
});
|
|
236
|
-
var tableSharedSchema =
|
|
237
|
-
title:
|
|
239
|
+
var tableSharedSchema = z5.object({
|
|
240
|
+
title: z5.string().optional().describe("Display title for table")
|
|
238
241
|
});
|
|
239
242
|
var tablePrimitiveSchema = tableSharedSchema.merge(
|
|
240
|
-
|
|
243
|
+
z5.object(
|
|
241
244
|
{
|
|
242
|
-
columns:
|
|
243
|
-
rows:
|
|
245
|
+
columns: z5.array(tableAlignmentSchema).optional(),
|
|
246
|
+
rows: z5.array(tableRowPrimitiveSchema)
|
|
244
247
|
},
|
|
245
248
|
{ description: "Table with primitive rows and optional alignment columns" }
|
|
246
249
|
)
|
|
247
250
|
);
|
|
248
251
|
var tableObjectSchema = tableSharedSchema.merge(
|
|
249
|
-
|
|
252
|
+
z5.object(
|
|
250
253
|
{
|
|
251
|
-
columns:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
+
columns: z5.union([
|
|
255
|
+
z5.array(tableAlignmentSchema),
|
|
256
|
+
z5.array(tableColumnObjectSchema)
|
|
254
257
|
]).optional(),
|
|
255
|
-
rows:
|
|
258
|
+
rows: z5.array(tableRowObjectSchema)
|
|
256
259
|
},
|
|
257
260
|
{
|
|
258
261
|
description: "Table with object rows and optional alignment or object columns"
|
|
259
262
|
}
|
|
260
263
|
)
|
|
261
264
|
);
|
|
262
|
-
var tableSchema = (description = "Table information") =>
|
|
265
|
+
var tableSchema = (description = "Table information") => z5.union([tablePrimitiveSchema, tableObjectSchema], { description });
|
|
263
266
|
|
|
264
267
|
// packages/models/src/lib/audit-output.ts
|
|
265
268
|
var auditValueSchema = nonnegativeNumberSchema.describe("Raw numeric value");
|
|
266
|
-
var auditDisplayValueSchema =
|
|
267
|
-
var auditDetailsSchema =
|
|
269
|
+
var auditDisplayValueSchema = z6.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
270
|
+
var auditDetailsSchema = z6.object(
|
|
268
271
|
{
|
|
269
|
-
issues:
|
|
272
|
+
issues: z6.array(issueSchema, { description: "List of findings" }).optional(),
|
|
270
273
|
table: tableSchema("Table of related findings").optional()
|
|
271
274
|
},
|
|
272
275
|
{ description: "Detailed information" }
|
|
273
276
|
);
|
|
274
|
-
var auditOutputSchema =
|
|
277
|
+
var auditOutputSchema = z6.object(
|
|
275
278
|
{
|
|
276
279
|
slug: slugSchema.describe("Reference to audit"),
|
|
277
280
|
displayValue: auditDisplayValueSchema,
|
|
@@ -281,7 +284,7 @@ var auditOutputSchema = z5.object(
|
|
|
281
284
|
},
|
|
282
285
|
{ description: "Audit information" }
|
|
283
286
|
);
|
|
284
|
-
var auditOutputsSchema =
|
|
287
|
+
var auditOutputsSchema = z6.array(auditOutputSchema, {
|
|
285
288
|
description: "List of JSON formatted audit output emitted by the runner process of a plugin"
|
|
286
289
|
}).refine(
|
|
287
290
|
(audits) => !getDuplicateSlugsInAudits2(audits),
|
|
@@ -298,13 +301,13 @@ function getDuplicateSlugsInAudits2(audits) {
|
|
|
298
301
|
}
|
|
299
302
|
|
|
300
303
|
// packages/models/src/lib/category-config.ts
|
|
301
|
-
import { z as
|
|
304
|
+
import { z as z7 } from "zod";
|
|
302
305
|
var categoryRefSchema = weightedRefSchema(
|
|
303
306
|
"Weighted references to audits and/or groups for the category",
|
|
304
307
|
"Slug of an audit or group (depending on `type`)"
|
|
305
308
|
).merge(
|
|
306
|
-
|
|
307
|
-
type:
|
|
309
|
+
z7.object({
|
|
310
|
+
type: z7.enum(["audit", "group"], {
|
|
308
311
|
description: "Discriminant for reference kind, affects where `slug` is looked up"
|
|
309
312
|
}),
|
|
310
313
|
plugin: slugSchema.describe(
|
|
@@ -325,8 +328,8 @@ var categoryConfigSchema = scorableSchema(
|
|
|
325
328
|
description: "Meta info for category"
|
|
326
329
|
})
|
|
327
330
|
).merge(
|
|
328
|
-
|
|
329
|
-
isBinary:
|
|
331
|
+
z7.object({
|
|
332
|
+
isBinary: z7.boolean({
|
|
330
333
|
description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
|
|
331
334
|
}).optional()
|
|
332
335
|
})
|
|
@@ -342,7 +345,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
|
|
|
342
345
|
metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
|
|
343
346
|
);
|
|
344
347
|
}
|
|
345
|
-
var categoriesSchema =
|
|
348
|
+
var categoriesSchema = z7.array(categoryConfigSchema, {
|
|
346
349
|
description: "Categorization of individual audits"
|
|
347
350
|
}).refine(
|
|
348
351
|
(categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
|
|
@@ -361,18 +364,18 @@ function getDuplicateSlugCategories(categories) {
|
|
|
361
364
|
}
|
|
362
365
|
|
|
363
366
|
// packages/models/src/lib/commit.ts
|
|
364
|
-
import { z as
|
|
365
|
-
var commitSchema =
|
|
367
|
+
import { z as z8 } from "zod";
|
|
368
|
+
var commitSchema = z8.object(
|
|
366
369
|
{
|
|
367
|
-
hash:
|
|
370
|
+
hash: z8.string({ description: "Commit SHA (full)" }).regex(
|
|
368
371
|
/^[\da-f]{40}$/,
|
|
369
372
|
"Commit SHA should be a 40-character hexadecimal string"
|
|
370
373
|
),
|
|
371
|
-
message:
|
|
372
|
-
date:
|
|
374
|
+
message: z8.string({ description: "Commit message" }),
|
|
375
|
+
date: z8.coerce.date({
|
|
373
376
|
description: "Date and time when commit was authored"
|
|
374
377
|
}),
|
|
375
|
-
author:
|
|
378
|
+
author: z8.string({
|
|
376
379
|
description: "Commit author name"
|
|
377
380
|
}).trim()
|
|
378
381
|
},
|
|
@@ -380,22 +383,22 @@ var commitSchema = z7.object(
|
|
|
380
383
|
);
|
|
381
384
|
|
|
382
385
|
// packages/models/src/lib/core-config.ts
|
|
383
|
-
import { z as
|
|
386
|
+
import { z as z14 } from "zod";
|
|
384
387
|
|
|
385
388
|
// packages/models/src/lib/persist-config.ts
|
|
386
|
-
import { z as
|
|
387
|
-
var formatSchema =
|
|
388
|
-
var persistConfigSchema =
|
|
389
|
+
import { z as z9 } from "zod";
|
|
390
|
+
var formatSchema = z9.enum(["json", "md"]);
|
|
391
|
+
var persistConfigSchema = z9.object({
|
|
389
392
|
outputDir: filePathSchema.describe("Artifacts folder").optional(),
|
|
390
393
|
filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
|
|
391
|
-
format:
|
|
394
|
+
format: z9.array(formatSchema).optional()
|
|
392
395
|
});
|
|
393
396
|
|
|
394
397
|
// packages/models/src/lib/plugin-config.ts
|
|
395
|
-
import { z as
|
|
398
|
+
import { z as z12 } from "zod";
|
|
396
399
|
|
|
397
400
|
// packages/models/src/lib/group.ts
|
|
398
|
-
import { z as
|
|
401
|
+
import { z as z10 } from "zod";
|
|
399
402
|
var groupRefSchema = weightedRefSchema(
|
|
400
403
|
"Weighted reference to a group",
|
|
401
404
|
"Reference slug to a group within this plugin (e.g. 'max-lines')"
|
|
@@ -412,7 +415,7 @@ var groupSchema = scorableSchema(
|
|
|
412
415
|
getDuplicateRefsInGroups,
|
|
413
416
|
duplicateRefsInGroupsErrorMsg
|
|
414
417
|
).merge(groupMetaSchema);
|
|
415
|
-
var groupsSchema =
|
|
418
|
+
var groupsSchema = z10.array(groupSchema, {
|
|
416
419
|
description: "List of groups"
|
|
417
420
|
}).optional().refine(
|
|
418
421
|
(groups) => !getDuplicateSlugsInGroups(groups),
|
|
@@ -440,14 +443,14 @@ function getDuplicateSlugsInGroups(groups) {
|
|
|
440
443
|
}
|
|
441
444
|
|
|
442
445
|
// packages/models/src/lib/runner-config.ts
|
|
443
|
-
import { z as
|
|
444
|
-
var outputTransformSchema =
|
|
445
|
-
var runnerConfigSchema =
|
|
446
|
+
import { z as z11 } from "zod";
|
|
447
|
+
var outputTransformSchema = z11.function().args(z11.unknown()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
|
|
448
|
+
var runnerConfigSchema = z11.object(
|
|
446
449
|
{
|
|
447
|
-
command:
|
|
450
|
+
command: z11.string({
|
|
448
451
|
description: "Shell command to execute"
|
|
449
452
|
}),
|
|
450
|
-
args:
|
|
453
|
+
args: z11.array(z11.string({ description: "Command arguments" })).optional(),
|
|
451
454
|
outputFile: filePathSchema.describe("Output path"),
|
|
452
455
|
outputTransform: outputTransformSchema.optional()
|
|
453
456
|
},
|
|
@@ -455,8 +458,8 @@ var runnerConfigSchema = z10.object(
|
|
|
455
458
|
description: "How to execute runner"
|
|
456
459
|
}
|
|
457
460
|
);
|
|
458
|
-
var onProgressSchema =
|
|
459
|
-
var runnerFunctionSchema =
|
|
461
|
+
var onProgressSchema = z11.function().args(z11.unknown()).returns(z11.void());
|
|
462
|
+
var runnerFunctionSchema = z11.function().args(onProgressSchema.optional()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
|
|
460
463
|
|
|
461
464
|
// packages/models/src/lib/plugin-config.ts
|
|
462
465
|
var pluginMetaSchema = packageVersionSchema().merge(
|
|
@@ -467,13 +470,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
|
|
|
467
470
|
description: "Plugin metadata"
|
|
468
471
|
})
|
|
469
472
|
).merge(
|
|
470
|
-
|
|
473
|
+
z12.object({
|
|
471
474
|
slug: slugSchema.describe("Unique plugin slug within core config"),
|
|
472
475
|
icon: materialIconSchema
|
|
473
476
|
})
|
|
474
477
|
);
|
|
475
|
-
var pluginDataSchema =
|
|
476
|
-
runner:
|
|
478
|
+
var pluginDataSchema = z12.object({
|
|
479
|
+
runner: z12.union([runnerConfigSchema, runnerFunctionSchema]),
|
|
477
480
|
audits: pluginAuditsSchema,
|
|
478
481
|
groups: groupsSchema
|
|
479
482
|
});
|
|
@@ -499,22 +502,22 @@ function getMissingRefsFromGroups(pluginCfg) {
|
|
|
499
502
|
}
|
|
500
503
|
|
|
501
504
|
// packages/models/src/lib/upload-config.ts
|
|
502
|
-
import { z as
|
|
503
|
-
var uploadConfigSchema =
|
|
505
|
+
import { z as z13 } from "zod";
|
|
506
|
+
var uploadConfigSchema = z13.object({
|
|
504
507
|
server: urlSchema.describe("URL of deployed portal API"),
|
|
505
|
-
apiKey:
|
|
508
|
+
apiKey: z13.string({
|
|
506
509
|
description: "API key with write access to portal (use `process.env` for security)"
|
|
507
510
|
}),
|
|
508
511
|
organization: slugSchema.describe(
|
|
509
512
|
"Organization slug from Code PushUp portal"
|
|
510
513
|
),
|
|
511
514
|
project: slugSchema.describe("Project slug from Code PushUp portal"),
|
|
512
|
-
timeout:
|
|
515
|
+
timeout: z13.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
|
|
513
516
|
});
|
|
514
517
|
|
|
515
518
|
// packages/models/src/lib/core-config.ts
|
|
516
|
-
var unrefinedCoreConfigSchema =
|
|
517
|
-
plugins:
|
|
519
|
+
var unrefinedCoreConfigSchema = z14.object({
|
|
520
|
+
plugins: z14.array(pluginConfigSchema, {
|
|
518
521
|
description: "List of plugins to be used (official, community-provided, or custom)"
|
|
519
522
|
}).min(1),
|
|
520
523
|
/** portal configuration for persisting results */
|
|
@@ -537,7 +540,7 @@ function refineCoreConfig(schema) {
|
|
|
537
540
|
}
|
|
538
541
|
|
|
539
542
|
// packages/models/src/lib/report.ts
|
|
540
|
-
import { z as
|
|
543
|
+
import { z as z15 } from "zod";
|
|
541
544
|
var auditReportSchema = auditSchema.merge(auditOutputSchema);
|
|
542
545
|
var pluginReportSchema = pluginMetaSchema.merge(
|
|
543
546
|
executionMetaSchema({
|
|
@@ -545,9 +548,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
|
|
|
545
548
|
descriptionDuration: "Duration of the plugin run in ms"
|
|
546
549
|
})
|
|
547
550
|
).merge(
|
|
548
|
-
|
|
549
|
-
audits:
|
|
550
|
-
groups:
|
|
551
|
+
z15.object({
|
|
552
|
+
audits: z15.array(auditReportSchema).min(1),
|
|
553
|
+
groups: z15.array(groupSchema).optional()
|
|
551
554
|
})
|
|
552
555
|
).refine(
|
|
553
556
|
(pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
|
|
@@ -581,10 +584,10 @@ var reportSchema = packageVersionSchema({
|
|
|
581
584
|
descriptionDuration: "Duration of the collect run in ms"
|
|
582
585
|
})
|
|
583
586
|
).merge(
|
|
584
|
-
|
|
587
|
+
z15.object(
|
|
585
588
|
{
|
|
586
|
-
categories:
|
|
587
|
-
plugins:
|
|
589
|
+
categories: z15.array(categoryConfigSchema),
|
|
590
|
+
plugins: z15.array(pluginReportSchema).min(1),
|
|
588
591
|
commit: commitSchema.describe("Git commit for which report was collected").nullable()
|
|
589
592
|
},
|
|
590
593
|
{ description: "Collect output data" }
|
|
@@ -600,40 +603,40 @@ var reportSchema = packageVersionSchema({
|
|
|
600
603
|
);
|
|
601
604
|
|
|
602
605
|
// packages/models/src/lib/reports-diff.ts
|
|
603
|
-
import { z as
|
|
606
|
+
import { z as z16 } from "zod";
|
|
604
607
|
function makeComparisonSchema(schema) {
|
|
605
608
|
const sharedDescription = schema.description || "Result";
|
|
606
|
-
return
|
|
609
|
+
return z16.object({
|
|
607
610
|
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
608
611
|
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
609
612
|
});
|
|
610
613
|
}
|
|
611
614
|
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
612
|
-
return
|
|
615
|
+
return z16.object(
|
|
613
616
|
{
|
|
614
|
-
changed:
|
|
615
|
-
unchanged:
|
|
616
|
-
added:
|
|
617
|
-
removed:
|
|
617
|
+
changed: z16.array(diffSchema),
|
|
618
|
+
unchanged: z16.array(resultSchema),
|
|
619
|
+
added: z16.array(resultSchema),
|
|
620
|
+
removed: z16.array(resultSchema)
|
|
618
621
|
},
|
|
619
622
|
{ description }
|
|
620
623
|
);
|
|
621
624
|
}
|
|
622
|
-
var scorableMetaSchema =
|
|
625
|
+
var scorableMetaSchema = z16.object({
|
|
623
626
|
slug: slugSchema,
|
|
624
627
|
title: titleSchema,
|
|
625
628
|
docsUrl: docsUrlSchema
|
|
626
629
|
});
|
|
627
630
|
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
628
|
-
|
|
631
|
+
z16.object({
|
|
629
632
|
plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
|
|
630
633
|
})
|
|
631
634
|
);
|
|
632
635
|
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
633
|
-
|
|
636
|
+
z16.object({
|
|
634
637
|
scores: makeComparisonSchema(scoreSchema).merge(
|
|
635
|
-
|
|
636
|
-
diff:
|
|
638
|
+
z16.object({
|
|
639
|
+
diff: z16.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
637
640
|
})
|
|
638
641
|
).describe("Score comparison")
|
|
639
642
|
})
|
|
@@ -644,10 +647,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
|
644
647
|
var categoryDiffSchema = scorableDiffSchema;
|
|
645
648
|
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
646
649
|
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
647
|
-
|
|
650
|
+
z16.object({
|
|
648
651
|
values: makeComparisonSchema(auditValueSchema).merge(
|
|
649
|
-
|
|
650
|
-
diff:
|
|
652
|
+
z16.object({
|
|
653
|
+
diff: z16.number().int().describe("Value change (`values.after - values.before`)")
|
|
651
654
|
})
|
|
652
655
|
).describe("Audit `value` comparison"),
|
|
653
656
|
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
@@ -656,16 +659,18 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
|
656
659
|
})
|
|
657
660
|
);
|
|
658
661
|
var categoryResultSchema = scorableMetaSchema.merge(
|
|
659
|
-
|
|
662
|
+
z16.object({ score: scoreSchema })
|
|
660
663
|
);
|
|
661
664
|
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
662
|
-
|
|
665
|
+
z16.object({ score: scoreSchema })
|
|
663
666
|
);
|
|
664
667
|
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
665
668
|
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
666
669
|
);
|
|
667
|
-
var reportsDiffSchema =
|
|
670
|
+
var reportsDiffSchema = z16.object({
|
|
668
671
|
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
672
|
+
portalUrl: urlSchema.optional().describe("Link to comparison page in Code PushUp portal"),
|
|
673
|
+
label: z16.string().optional().describe("Label (e.g. project name)"),
|
|
669
674
|
categories: makeArraysComparisonSchema(
|
|
670
675
|
categoryDiffSchema,
|
|
671
676
|
categoryResultSchema,
|
|
@@ -735,8 +740,24 @@ function comparePairs(pairs, equalsFn) {
|
|
|
735
740
|
);
|
|
736
741
|
}
|
|
737
742
|
|
|
743
|
+
// packages/utils/src/lib/errors.ts
|
|
744
|
+
function stringifyError(error) {
|
|
745
|
+
if (error instanceof Error) {
|
|
746
|
+
if (error.name === "Error" || error.message.startsWith(error.name)) {
|
|
747
|
+
return error.message;
|
|
748
|
+
}
|
|
749
|
+
return `${error.name}: ${error.message}`;
|
|
750
|
+
}
|
|
751
|
+
if (typeof error === "string") {
|
|
752
|
+
return error;
|
|
753
|
+
}
|
|
754
|
+
return JSON.stringify(error);
|
|
755
|
+
}
|
|
756
|
+
|
|
738
757
|
// packages/utils/src/lib/execute-process.ts
|
|
739
|
-
import {
|
|
758
|
+
import {
|
|
759
|
+
spawn
|
|
760
|
+
} from "node:child_process";
|
|
740
761
|
|
|
741
762
|
// packages/utils/src/lib/reports/utils.ts
|
|
742
763
|
import ansis from "ansis";
|
|
@@ -752,6 +773,7 @@ var FOOTER_PREFIX = "Made with \u2764 by";
|
|
|
752
773
|
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
753
774
|
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
754
775
|
var REPORT_HEADLINE_TEXT = "Code PushUp Report";
|
|
776
|
+
var CODE_PUSHUP_UNICODE_LOGO = "<\u2713>";
|
|
755
777
|
var REPORT_RAW_OVERVIEW_TABLE_HEADERS = [
|
|
756
778
|
"Category",
|
|
757
779
|
"Score",
|
|
@@ -822,9 +844,17 @@ function severityMarker(severity) {
|
|
|
822
844
|
}
|
|
823
845
|
return "\u2139\uFE0F";
|
|
824
846
|
}
|
|
847
|
+
var MIN_NON_ZERO_RESULT = 0.1;
|
|
848
|
+
function roundValue(value) {
|
|
849
|
+
const roundedValue = Math.round(value * 10) / 10;
|
|
850
|
+
if (roundedValue === 0 && value !== 0) {
|
|
851
|
+
return MIN_NON_ZERO_RESULT * Math.sign(value);
|
|
852
|
+
}
|
|
853
|
+
return roundedValue;
|
|
854
|
+
}
|
|
825
855
|
function formatScoreChange(diff) {
|
|
826
856
|
const marker = getDiffMarker(diff);
|
|
827
|
-
const text = formatDiffNumber(
|
|
857
|
+
const text = formatDiffNumber(roundValue(diff * 100));
|
|
828
858
|
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
829
859
|
}
|
|
830
860
|
function formatValueChange({
|
|
@@ -832,7 +862,7 @@ function formatValueChange({
|
|
|
832
862
|
scores
|
|
833
863
|
}) {
|
|
834
864
|
const marker = getDiffMarker(values.diff);
|
|
835
|
-
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY :
|
|
865
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : roundValue(values.diff / values.before * 100);
|
|
836
866
|
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
837
867
|
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
838
868
|
}
|
|
@@ -863,12 +893,12 @@ function countCategoryAudits(refs, plugins) {
|
|
|
863
893
|
}, 0);
|
|
864
894
|
}
|
|
865
895
|
function compareCategoryAuditsAndGroups(a, b) {
|
|
866
|
-
if (a.weight !== b.weight) {
|
|
867
|
-
return b.weight - a.weight;
|
|
868
|
-
}
|
|
869
896
|
if (a.score !== b.score) {
|
|
870
897
|
return a.score - b.score;
|
|
871
898
|
}
|
|
899
|
+
if (a.weight !== b.weight) {
|
|
900
|
+
return b.weight - a.weight;
|
|
901
|
+
}
|
|
872
902
|
if ("value" in a && "value" in b && a.value !== b.value) {
|
|
873
903
|
return b.value - a.value;
|
|
874
904
|
}
|
|
@@ -960,25 +990,29 @@ var ProcessError = class extends Error {
|
|
|
960
990
|
}
|
|
961
991
|
};
|
|
962
992
|
function executeProcess(cfg) {
|
|
963
|
-
const {
|
|
964
|
-
const { onStdout, onError, onComplete } = observer ?? {};
|
|
993
|
+
const { command, args, observer, ignoreExitCode = false, ...options } = cfg;
|
|
994
|
+
const { onStdout, onStderr, onError, onComplete } = observer ?? {};
|
|
965
995
|
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
966
996
|
const start = performance.now();
|
|
967
997
|
return new Promise((resolve, reject) => {
|
|
968
|
-
const
|
|
998
|
+
const spawnedProcess = spawn(command, args ?? [], {
|
|
999
|
+
shell: true,
|
|
1000
|
+
...options
|
|
1001
|
+
});
|
|
969
1002
|
let stdout = "";
|
|
970
1003
|
let stderr = "";
|
|
971
|
-
|
|
1004
|
+
spawnedProcess.stdout.on("data", (data) => {
|
|
972
1005
|
stdout += String(data);
|
|
973
|
-
onStdout?.(String(data));
|
|
1006
|
+
onStdout?.(String(data), spawnedProcess);
|
|
974
1007
|
});
|
|
975
|
-
|
|
1008
|
+
spawnedProcess.stderr.on("data", (data) => {
|
|
976
1009
|
stderr += String(data);
|
|
1010
|
+
onStderr?.(String(data), spawnedProcess);
|
|
977
1011
|
});
|
|
978
|
-
|
|
1012
|
+
spawnedProcess.on("error", (err) => {
|
|
979
1013
|
stderr += err.toString();
|
|
980
1014
|
});
|
|
981
|
-
|
|
1015
|
+
spawnedProcess.on("close", (code2) => {
|
|
982
1016
|
const timings = { date, duration: calcDuration(start) };
|
|
983
1017
|
if (code2 === 0 || ignoreExitCode) {
|
|
984
1018
|
onComplete?.();
|
|
@@ -1881,7 +1915,33 @@ var html = {
|
|
|
1881
1915
|
};
|
|
1882
1916
|
|
|
1883
1917
|
// packages/utils/src/lib/reports/formatting.ts
|
|
1884
|
-
import {
|
|
1918
|
+
import {
|
|
1919
|
+
MarkdownDocument,
|
|
1920
|
+
md as md2
|
|
1921
|
+
} from "build-md";
|
|
1922
|
+
import { posix as pathPosix } from "node:path";
|
|
1923
|
+
|
|
1924
|
+
// packages/utils/src/lib/reports/ide-environment.ts
|
|
1925
|
+
function getEnvironmentType() {
|
|
1926
|
+
if (isVSCode()) {
|
|
1927
|
+
return "vscode";
|
|
1928
|
+
}
|
|
1929
|
+
if (isGitHub()) {
|
|
1930
|
+
return "github";
|
|
1931
|
+
}
|
|
1932
|
+
return "other";
|
|
1933
|
+
}
|
|
1934
|
+
function isVSCode() {
|
|
1935
|
+
return process.env["TERM_PROGRAM"] === "vscode";
|
|
1936
|
+
}
|
|
1937
|
+
function isGitHub() {
|
|
1938
|
+
return process.env["GITHUB_ACTIONS"] === "true";
|
|
1939
|
+
}
|
|
1940
|
+
function getGitHubBaseUrl() {
|
|
1941
|
+
return `${process.env["GITHUB_SERVER_URL"]}/${process.env["GITHUB_REPOSITORY"]}/blob/${process.env["GITHUB_SHA"]}`;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
// packages/utils/src/lib/reports/formatting.ts
|
|
1885
1945
|
function tableSection(tableData, options) {
|
|
1886
1946
|
if (tableData.rows.length === 0) {
|
|
1887
1947
|
return null;
|
|
@@ -1919,6 +1979,44 @@ function metaDescription(audit) {
|
|
|
1919
1979
|
}
|
|
1920
1980
|
return "";
|
|
1921
1981
|
}
|
|
1982
|
+
function linkToLocalSourceForIde(source, options) {
|
|
1983
|
+
const { file, position } = source;
|
|
1984
|
+
const { outputDir } = options ?? {};
|
|
1985
|
+
if (!outputDir) {
|
|
1986
|
+
return md2.code(file);
|
|
1987
|
+
}
|
|
1988
|
+
return md2.link(formatFileLink(file, position, outputDir), md2.code(file));
|
|
1989
|
+
}
|
|
1990
|
+
function formatSourceLine(position) {
|
|
1991
|
+
if (!position) {
|
|
1992
|
+
return "";
|
|
1993
|
+
}
|
|
1994
|
+
const { startLine, endLine } = position;
|
|
1995
|
+
return endLine && startLine !== endLine ? `${startLine}-${endLine}` : `${startLine}`;
|
|
1996
|
+
}
|
|
1997
|
+
function formatGitHubLink(file, position) {
|
|
1998
|
+
const baseUrl = getGitHubBaseUrl();
|
|
1999
|
+
if (!position) {
|
|
2000
|
+
return `${baseUrl}/${file}`;
|
|
2001
|
+
}
|
|
2002
|
+
const { startLine, endLine, startColumn, endColumn } = position;
|
|
2003
|
+
const start = startColumn ? `L${startLine}C${startColumn}` : `L${startLine}`;
|
|
2004
|
+
const end = endLine ? endColumn ? `L${endLine}C${endColumn}` : `L${endLine}` : "";
|
|
2005
|
+
const lineRange = end && start !== end ? `${start}-${end}` : start;
|
|
2006
|
+
return `${baseUrl}/${file}#${lineRange}`;
|
|
2007
|
+
}
|
|
2008
|
+
function formatFileLink(file, position, outputDir) {
|
|
2009
|
+
const relativePath = pathPosix.relative(outputDir, file);
|
|
2010
|
+
const env = getEnvironmentType();
|
|
2011
|
+
switch (env) {
|
|
2012
|
+
case "vscode":
|
|
2013
|
+
return position ? `${relativePath}#L${position.startLine}` : relativePath;
|
|
2014
|
+
case "github":
|
|
2015
|
+
return formatGitHubLink(file, position);
|
|
2016
|
+
default:
|
|
2017
|
+
return relativePath;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
1922
2020
|
|
|
1923
2021
|
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
1924
2022
|
import { MarkdownDocument as MarkdownDocument2, md as md3 } from "build-md";
|
|
@@ -2120,16 +2218,16 @@ function auditDetailsAuditValue({
|
|
|
2120
2218
|
String(displayValue ?? value)
|
|
2121
2219
|
)} (score: ${formatReportScore(score)})`;
|
|
2122
2220
|
}
|
|
2123
|
-
function generateMdReport(report) {
|
|
2221
|
+
function generateMdReport(report, options) {
|
|
2124
2222
|
return new MarkdownDocument3().heading(HIERARCHY.level_1, REPORT_HEADLINE_TEXT).$if(
|
|
2125
2223
|
report.categories.length > 0,
|
|
2126
2224
|
(doc) => doc.$concat(
|
|
2127
2225
|
categoriesOverviewSection(report),
|
|
2128
2226
|
categoriesDetailsSection(report)
|
|
2129
2227
|
)
|
|
2130
|
-
).$concat(auditsSection(report), aboutSection(report)).rule().paragraph(md4`${FOOTER_PREFIX} ${md4.link(README_LINK, "Code PushUp")}`).toString();
|
|
2228
|
+
).$concat(auditsSection(report, options), aboutSection(report)).rule().paragraph(md4`${FOOTER_PREFIX} ${md4.link(README_LINK, "Code PushUp")}`).toString();
|
|
2131
2229
|
}
|
|
2132
|
-
function auditDetailsIssues(issues = []) {
|
|
2230
|
+
function auditDetailsIssues(issues = [], options) {
|
|
2133
2231
|
if (issues.length === 0) {
|
|
2134
2232
|
return null;
|
|
2135
2233
|
}
|
|
@@ -2145,39 +2243,36 @@ function auditDetailsIssues(issues = []) {
|
|
|
2145
2243
|
if (!source) {
|
|
2146
2244
|
return [severity, message];
|
|
2147
2245
|
}
|
|
2148
|
-
const file =
|
|
2246
|
+
const file = linkToLocalSourceForIde(source, options);
|
|
2149
2247
|
if (!source.position) {
|
|
2150
2248
|
return [severity, message, file];
|
|
2151
2249
|
}
|
|
2152
|
-
const
|
|
2153
|
-
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
2250
|
+
const line = formatSourceLine(source.position);
|
|
2154
2251
|
return [severity, message, file, line];
|
|
2155
2252
|
})
|
|
2156
2253
|
);
|
|
2157
2254
|
}
|
|
2158
|
-
function auditDetails(audit) {
|
|
2255
|
+
function auditDetails(audit, options) {
|
|
2159
2256
|
const { table: table2, issues = [] } = audit.details ?? {};
|
|
2160
2257
|
const detailsValue = auditDetailsAuditValue(audit);
|
|
2161
2258
|
if (issues.length === 0 && !table2?.rows.length) {
|
|
2162
2259
|
return new MarkdownDocument3().paragraph(detailsValue);
|
|
2163
2260
|
}
|
|
2164
2261
|
const tableSectionContent = table2 && tableSection(table2);
|
|
2165
|
-
const issuesSectionContent = issues.length > 0 && auditDetailsIssues(issues);
|
|
2262
|
+
const issuesSectionContent = issues.length > 0 && auditDetailsIssues(issues, options);
|
|
2166
2263
|
return new MarkdownDocument3().details(
|
|
2167
2264
|
detailsValue,
|
|
2168
2265
|
new MarkdownDocument3().$concat(tableSectionContent, issuesSectionContent)
|
|
2169
2266
|
);
|
|
2170
2267
|
}
|
|
2171
|
-
function auditsSection({
|
|
2172
|
-
plugins
|
|
2173
|
-
}) {
|
|
2268
|
+
function auditsSection({ plugins }, options) {
|
|
2174
2269
|
return new MarkdownDocument3().heading(HIERARCHY.level_2, "\u{1F6E1}\uFE0F Audits").$foreach(
|
|
2175
2270
|
plugins.flatMap(
|
|
2176
2271
|
(plugin) => plugin.audits.map((audit) => ({ ...audit, plugin }))
|
|
2177
2272
|
),
|
|
2178
2273
|
(doc, { plugin, ...audit }) => {
|
|
2179
2274
|
const auditTitle = `${audit.title} (${plugin.title})`;
|
|
2180
|
-
const detailsContent = auditDetails(audit);
|
|
2275
|
+
const detailsContent = auditDetails(audit, options);
|
|
2181
2276
|
const descriptionContent = metaDescription(audit);
|
|
2182
2277
|
return doc.heading(HIERARCHY.level_3, auditTitle).$concat(detailsContent).paragraph(descriptionContent);
|
|
2183
2278
|
}
|
|
@@ -2241,19 +2336,111 @@ function reportMetaTable({
|
|
|
2241
2336
|
|
|
2242
2337
|
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
2243
2338
|
import {
|
|
2244
|
-
MarkdownDocument as
|
|
2245
|
-
md as
|
|
2339
|
+
MarkdownDocument as MarkdownDocument5,
|
|
2340
|
+
md as md6
|
|
2246
2341
|
} from "build-md";
|
|
2342
|
+
|
|
2343
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts
|
|
2344
|
+
import { MarkdownDocument as MarkdownDocument4, md as md5 } from "build-md";
|
|
2247
2345
|
var MAX_ROWS = 100;
|
|
2248
|
-
function
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
).
|
|
2346
|
+
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
2347
|
+
const pluralizedCount = changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`;
|
|
2348
|
+
const pluralizedVerb = unchanged.length === 1 ? "is" : "are";
|
|
2349
|
+
return `${pluralizedCount} ${pluralizedVerb} unchanged.`;
|
|
2350
|
+
}
|
|
2351
|
+
function summarizeDiffOutcomes(outcomes, token) {
|
|
2352
|
+
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
2353
|
+
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
2354
|
+
).map(([outcome, count]) => {
|
|
2355
|
+
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
2356
|
+
token,
|
|
2357
|
+
count
|
|
2358
|
+
)}`;
|
|
2359
|
+
switch (outcome) {
|
|
2360
|
+
case "positive":
|
|
2361
|
+
return `\u{1F44D} ${formattedCount} improved`;
|
|
2362
|
+
case "negative":
|
|
2363
|
+
return `\u{1F44E} ${formattedCount} regressed`;
|
|
2364
|
+
case "mixed":
|
|
2365
|
+
return `${formattedCount} changed without impacting score`;
|
|
2366
|
+
}
|
|
2367
|
+
}).join(", ");
|
|
2368
|
+
}
|
|
2369
|
+
function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
|
|
2370
|
+
if (changed.length === 0) {
|
|
2371
|
+
return new MarkdownDocument4().paragraph(
|
|
2372
|
+
summarizeUnchanged(token, { changed, unchanged })
|
|
2373
|
+
);
|
|
2374
|
+
}
|
|
2375
|
+
return new MarkdownDocument4().table(columns, rows.slice(0, MAX_ROWS)).paragraph(
|
|
2376
|
+
changed.length > MAX_ROWS && md5.italic(
|
|
2377
|
+
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
2378
|
+
token
|
|
2379
|
+
)} are listed above for brevity.`
|
|
2380
|
+
)
|
|
2381
|
+
).paragraph(
|
|
2382
|
+
unchanged.length > 0 && summarizeUnchanged(token, { changed, unchanged })
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
function formatTitle({
|
|
2386
|
+
title,
|
|
2387
|
+
docsUrl
|
|
2388
|
+
}) {
|
|
2389
|
+
if (docsUrl) {
|
|
2390
|
+
return md5.link(docsUrl, title);
|
|
2391
|
+
}
|
|
2392
|
+
return title;
|
|
2393
|
+
}
|
|
2394
|
+
function formatPortalLink(portalUrl) {
|
|
2395
|
+
return portalUrl && md5.link(portalUrl, "\u{1F575}\uFE0F See full comparison in Code PushUp portal \u{1F50D}");
|
|
2396
|
+
}
|
|
2397
|
+
function sortChanges(changes) {
|
|
2398
|
+
return [...changes].sort(
|
|
2399
|
+
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
function getDiffChanges(diff) {
|
|
2403
|
+
return [
|
|
2404
|
+
...diff.categories.changed,
|
|
2405
|
+
...diff.groups.changed,
|
|
2406
|
+
...diff.audits.changed
|
|
2407
|
+
];
|
|
2408
|
+
}
|
|
2409
|
+
function changesToDiffOutcomes(changes) {
|
|
2410
|
+
return changes.map((change) => {
|
|
2411
|
+
if (change.scores.diff > 0) {
|
|
2412
|
+
return "positive";
|
|
2413
|
+
}
|
|
2414
|
+
if (change.scores.diff < 0) {
|
|
2415
|
+
return "negative";
|
|
2416
|
+
}
|
|
2417
|
+
if (change.values != null && change.values.diff !== 0) {
|
|
2418
|
+
return "mixed";
|
|
2419
|
+
}
|
|
2420
|
+
return "unchanged";
|
|
2421
|
+
});
|
|
2422
|
+
}
|
|
2423
|
+
function mergeDiffOutcomes(outcomes) {
|
|
2424
|
+
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
2425
|
+
return "unchanged";
|
|
2426
|
+
}
|
|
2427
|
+
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
2428
|
+
return "positive";
|
|
2429
|
+
}
|
|
2430
|
+
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
2431
|
+
return "negative";
|
|
2432
|
+
}
|
|
2433
|
+
return "mixed";
|
|
2255
2434
|
}
|
|
2256
|
-
function
|
|
2435
|
+
function countDiffOutcomes(outcomes) {
|
|
2436
|
+
return {
|
|
2437
|
+
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
2438
|
+
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
2439
|
+
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
2440
|
+
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
function formatReportOutcome(outcome, commits) {
|
|
2257
2444
|
const outcomeTexts = {
|
|
2258
2445
|
positive: md5`🥳 Code PushUp report has ${md5.bold("improved")}`,
|
|
2259
2446
|
negative: md5`😟 Code PushUp report has ${md5.bold("regressed")}`,
|
|
@@ -2262,36 +2449,91 @@ function createDiffHeaderSection(diff, portalUrl) {
|
|
|
2262
2449
|
)}`,
|
|
2263
2450
|
unchanged: md5`😐 Code PushUp report is ${md5.bold("unchanged")}`
|
|
2264
2451
|
};
|
|
2452
|
+
if (commits) {
|
|
2453
|
+
const commitsText = `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
2454
|
+
return md5`${outcomeTexts[outcome]} – ${commitsText}.`;
|
|
2455
|
+
}
|
|
2456
|
+
return md5`${outcomeTexts[outcome]}.`;
|
|
2457
|
+
}
|
|
2458
|
+
function compareDiffsBy(type, a, b) {
|
|
2459
|
+
return sumScoreChanges(b[type].changed) - sumScoreChanges(a[type].changed) || sumConfigChanges(b[type]) - sumConfigChanges(a[type]);
|
|
2460
|
+
}
|
|
2461
|
+
function sumScoreChanges(changes) {
|
|
2462
|
+
return changes.reduce(
|
|
2463
|
+
(acc, { scores }) => acc + Math.abs(scores.diff),
|
|
2464
|
+
0
|
|
2465
|
+
);
|
|
2466
|
+
}
|
|
2467
|
+
function sumConfigChanges({
|
|
2468
|
+
added,
|
|
2469
|
+
removed
|
|
2470
|
+
}) {
|
|
2471
|
+
return added.length + removed.length;
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
2475
|
+
function generateMdReportsDiff(diff) {
|
|
2476
|
+
return new MarkdownDocument5().$concat(
|
|
2477
|
+
createDiffHeaderSection(diff),
|
|
2478
|
+
createDiffCategoriesSection(diff),
|
|
2479
|
+
createDiffDetailsSection(diff)
|
|
2480
|
+
).toString();
|
|
2481
|
+
}
|
|
2482
|
+
function generateMdReportsDiffForMonorepo(diffs) {
|
|
2483
|
+
const diffsWithOutcomes = diffs.map((diff) => ({
|
|
2484
|
+
...diff,
|
|
2485
|
+
outcome: mergeDiffOutcomes(changesToDiffOutcomes(getDiffChanges(diff)))
|
|
2486
|
+
})).sort(
|
|
2487
|
+
(a, b) => compareDiffsBy("categories", a, b) || compareDiffsBy("groups", a, b) || compareDiffsBy("audits", a, b) || a.label.localeCompare(b.label)
|
|
2488
|
+
);
|
|
2489
|
+
const unchanged = diffsWithOutcomes.filter(
|
|
2490
|
+
({ outcome }) => outcome === "unchanged"
|
|
2491
|
+
);
|
|
2492
|
+
const changed = diffsWithOutcomes.filter((diff) => !unchanged.includes(diff));
|
|
2493
|
+
return new MarkdownDocument5().$concat(
|
|
2494
|
+
createDiffHeaderSection(diffs),
|
|
2495
|
+
...changed.map(createDiffProjectSection)
|
|
2496
|
+
).$if(
|
|
2497
|
+
unchanged.length > 0,
|
|
2498
|
+
(doc) => doc.rule().paragraph(summarizeUnchanged("project", { unchanged, changed }))
|
|
2499
|
+
).toString();
|
|
2500
|
+
}
|
|
2501
|
+
function createDiffHeaderSection(diff) {
|
|
2265
2502
|
const outcome = mergeDiffOutcomes(
|
|
2266
|
-
changesToDiffOutcomes(
|
|
2267
|
-
...diff.categories.changed,
|
|
2268
|
-
...diff.groups.changed,
|
|
2269
|
-
...diff.audits.changed
|
|
2270
|
-
])
|
|
2503
|
+
changesToDiffOutcomes(toArray(diff).flatMap(getDiffChanges))
|
|
2271
2504
|
);
|
|
2272
|
-
const
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2505
|
+
const commits = Array.isArray(diff) ? diff[0]?.commits : diff.commits;
|
|
2506
|
+
const portalUrl = Array.isArray(diff) ? void 0 : diff.portalUrl;
|
|
2507
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_1, "Code PushUp").paragraph(formatReportOutcome(outcome, commits)).paragraph(formatPortalLink(portalUrl));
|
|
2508
|
+
}
|
|
2509
|
+
function createDiffProjectSection(diff) {
|
|
2510
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_2, md6`💼 Project ${md6.code(diff.label)}`).paragraph(formatReportOutcome(diff.outcome)).paragraph(formatPortalLink(diff.portalUrl)).$concat(
|
|
2511
|
+
createDiffCategoriesSection(diff, {
|
|
2512
|
+
skipHeading: true,
|
|
2513
|
+
skipUnchanged: true
|
|
2514
|
+
}),
|
|
2515
|
+
createDiffDetailsSection(diff, HIERARCHY.level_3)
|
|
2277
2516
|
);
|
|
2278
2517
|
}
|
|
2279
|
-
function createDiffCategoriesSection(diff) {
|
|
2518
|
+
function createDiffCategoriesSection(diff, options) {
|
|
2280
2519
|
const { changed, unchanged, added } = diff.categories;
|
|
2520
|
+
const { skipHeading, skipUnchanged } = options ?? {};
|
|
2281
2521
|
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
2282
2522
|
const hasChanges = unchanged.length < categoriesCount;
|
|
2283
2523
|
if (categoriesCount === 0) {
|
|
2284
2524
|
return null;
|
|
2285
2525
|
}
|
|
2286
|
-
const columns =
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2526
|
+
const [columns, rows] = createCategoriesTable(diff, {
|
|
2527
|
+
hasChanges,
|
|
2528
|
+
skipUnchanged
|
|
2529
|
+
});
|
|
2530
|
+
return new MarkdownDocument5().heading(HIERARCHY.level_2, !skipHeading && "\u{1F3F7}\uFE0F Categories").table(columns, rows).paragraph(added.length > 0 && md6.italic("(\\*) New category.")).paragraph(
|
|
2531
|
+
skipUnchanged && unchanged.length > 0 && summarizeUnchanged("category", { changed, unchanged })
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
function createCategoriesTable(diff, options) {
|
|
2535
|
+
const { changed, unchanged, added } = diff.categories;
|
|
2536
|
+
const { hasChanges, skipUnchanged } = options;
|
|
2295
2537
|
const rows = [
|
|
2296
2538
|
...sortChanges(changed).map((category) => [
|
|
2297
2539
|
formatTitle(category),
|
|
@@ -2303,27 +2545,55 @@ function createDiffCategoriesSection(diff) {
|
|
|
2303
2545
|
]),
|
|
2304
2546
|
...added.map((category) => [
|
|
2305
2547
|
formatTitle(category),
|
|
2306
|
-
|
|
2548
|
+
md6.italic("n/a (\\*)"),
|
|
2307
2549
|
formatScoreWithColor(category.score),
|
|
2308
|
-
|
|
2550
|
+
md6.italic("n/a (\\*)")
|
|
2309
2551
|
]),
|
|
2310
|
-
...unchanged.map((category) => [
|
|
2552
|
+
...skipUnchanged ? [] : unchanged.map((category) => [
|
|
2311
2553
|
formatTitle(category),
|
|
2312
2554
|
formatScoreWithColor(category.score, { skipBold: true }),
|
|
2313
2555
|
formatScoreWithColor(category.score),
|
|
2314
2556
|
"\u2013"
|
|
2315
2557
|
])
|
|
2316
2558
|
];
|
|
2317
|
-
|
|
2559
|
+
if (rows.length === 0) {
|
|
2560
|
+
return [[], []];
|
|
2561
|
+
}
|
|
2562
|
+
const columns = [
|
|
2563
|
+
{ heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
|
|
2564
|
+
{
|
|
2565
|
+
heading: hasChanges ? "\u2B50 Previous score" : "\u2B50 Score",
|
|
2566
|
+
alignment: "center"
|
|
2567
|
+
},
|
|
2568
|
+
{ heading: "\u2B50 Current score", alignment: "center" },
|
|
2569
|
+
{ heading: "\u{1F504} Score change", alignment: "center" }
|
|
2570
|
+
];
|
|
2571
|
+
return [
|
|
2318
2572
|
hasChanges ? columns : columns.slice(0, 2),
|
|
2319
2573
|
rows.map((row) => hasChanges ? row : row.slice(0, 2))
|
|
2320
|
-
|
|
2574
|
+
];
|
|
2321
2575
|
}
|
|
2322
|
-
function
|
|
2576
|
+
function createDiffDetailsSection(diff, level = HIERARCHY.level_2) {
|
|
2577
|
+
if (diff.groups.changed.length + diff.audits.changed.length === 0) {
|
|
2578
|
+
return null;
|
|
2579
|
+
}
|
|
2580
|
+
const summary = ["group", "audit"].map(
|
|
2581
|
+
(token) => summarizeDiffOutcomes(
|
|
2582
|
+
changesToDiffOutcomes(diff[`${token}s`].changed),
|
|
2583
|
+
token
|
|
2584
|
+
)
|
|
2585
|
+
).filter(Boolean).join(", ");
|
|
2586
|
+
const details2 = new MarkdownDocument5().$concat(
|
|
2587
|
+
createDiffGroupsSection(diff, level),
|
|
2588
|
+
createDiffAuditsSection(diff, level)
|
|
2589
|
+
);
|
|
2590
|
+
return new MarkdownDocument5().details(summary, details2);
|
|
2591
|
+
}
|
|
2592
|
+
function createDiffGroupsSection(diff, level) {
|
|
2323
2593
|
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
2324
2594
|
return null;
|
|
2325
2595
|
}
|
|
2326
|
-
return new
|
|
2596
|
+
return new MarkdownDocument5().heading(level, "\u{1F5C3}\uFE0F Groups").$concat(
|
|
2327
2597
|
createGroupsOrAuditsDetails(
|
|
2328
2598
|
"group",
|
|
2329
2599
|
diff.groups,
|
|
@@ -2344,8 +2614,8 @@ function createDiffGroupsSection(diff) {
|
|
|
2344
2614
|
)
|
|
2345
2615
|
);
|
|
2346
2616
|
}
|
|
2347
|
-
function createDiffAuditsSection(diff) {
|
|
2348
|
-
return new
|
|
2617
|
+
function createDiffAuditsSection(diff, level) {
|
|
2618
|
+
return new MarkdownDocument5().heading(level, "\u{1F6E1}\uFE0F Audits").$concat(
|
|
2349
2619
|
createGroupsOrAuditsDetails(
|
|
2350
2620
|
"audit",
|
|
2351
2621
|
diff.audits,
|
|
@@ -2360,7 +2630,7 @@ function createDiffAuditsSection(diff) {
|
|
|
2360
2630
|
formatTitle(audit.plugin),
|
|
2361
2631
|
formatTitle(audit),
|
|
2362
2632
|
`${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
2363
|
-
|
|
2633
|
+
md6`${scoreMarker(audit.scores.after, "square")} ${md6.bold(
|
|
2364
2634
|
audit.displayValues.after || audit.values.after.toString()
|
|
2365
2635
|
)}`,
|
|
2366
2636
|
formatValueChange(audit)
|
|
@@ -2368,96 +2638,6 @@ function createDiffAuditsSection(diff) {
|
|
|
2368
2638
|
)
|
|
2369
2639
|
);
|
|
2370
2640
|
}
|
|
2371
|
-
function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
|
|
2372
|
-
if (changed.length === 0) {
|
|
2373
|
-
return new MarkdownDocument4().paragraph(
|
|
2374
|
-
summarizeUnchanged(token, { changed, unchanged })
|
|
2375
|
-
);
|
|
2376
|
-
}
|
|
2377
|
-
return new MarkdownDocument4().details(
|
|
2378
|
-
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
2379
|
-
md5`${md5.table(columns, rows.slice(0, MAX_ROWS))}${changed.length > MAX_ROWS ? md5.paragraph(
|
|
2380
|
-
md5.italic(
|
|
2381
|
-
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
2382
|
-
token
|
|
2383
|
-
)} are listed above for brevity.`
|
|
2384
|
-
)
|
|
2385
|
-
) : ""}${unchanged.length > 0 ? md5.paragraph(summarizeUnchanged(token, { changed, unchanged })) : ""}`
|
|
2386
|
-
);
|
|
2387
|
-
}
|
|
2388
|
-
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
2389
|
-
return [
|
|
2390
|
-
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
2391
|
-
unchanged.length === 1 ? "is" : "are",
|
|
2392
|
-
"unchanged."
|
|
2393
|
-
].join(" ");
|
|
2394
|
-
}
|
|
2395
|
-
function summarizeDiffOutcomes(outcomes, token) {
|
|
2396
|
-
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
2397
|
-
(entry) => entry[0] !== "unchanged" && entry[1] > 0
|
|
2398
|
-
).map(([outcome, count]) => {
|
|
2399
|
-
const formattedCount = `<strong>${count}</strong> ${pluralize(
|
|
2400
|
-
token,
|
|
2401
|
-
count
|
|
2402
|
-
)}`;
|
|
2403
|
-
switch (outcome) {
|
|
2404
|
-
case "positive":
|
|
2405
|
-
return `\u{1F44D} ${formattedCount} improved`;
|
|
2406
|
-
case "negative":
|
|
2407
|
-
return `\u{1F44E} ${formattedCount} regressed`;
|
|
2408
|
-
case "mixed":
|
|
2409
|
-
return `${formattedCount} changed without impacting score`;
|
|
2410
|
-
}
|
|
2411
|
-
}).join(", ");
|
|
2412
|
-
}
|
|
2413
|
-
function formatTitle({
|
|
2414
|
-
title,
|
|
2415
|
-
docsUrl
|
|
2416
|
-
}) {
|
|
2417
|
-
if (docsUrl) {
|
|
2418
|
-
return md5.link(docsUrl, title);
|
|
2419
|
-
}
|
|
2420
|
-
return title;
|
|
2421
|
-
}
|
|
2422
|
-
function sortChanges(changes) {
|
|
2423
|
-
return [...changes].sort(
|
|
2424
|
-
(a, b) => Math.abs(b.scores.diff) - Math.abs(a.scores.diff) || Math.abs(b.values?.diff ?? 0) - Math.abs(a.values?.diff ?? 0)
|
|
2425
|
-
);
|
|
2426
|
-
}
|
|
2427
|
-
function changesToDiffOutcomes(changes) {
|
|
2428
|
-
return changes.map((change) => {
|
|
2429
|
-
if (change.scores.diff > 0) {
|
|
2430
|
-
return "positive";
|
|
2431
|
-
}
|
|
2432
|
-
if (change.scores.diff < 0) {
|
|
2433
|
-
return "negative";
|
|
2434
|
-
}
|
|
2435
|
-
if (change.values != null && change.values.diff !== 0) {
|
|
2436
|
-
return "mixed";
|
|
2437
|
-
}
|
|
2438
|
-
return "unchanged";
|
|
2439
|
-
});
|
|
2440
|
-
}
|
|
2441
|
-
function mergeDiffOutcomes(outcomes) {
|
|
2442
|
-
if (outcomes.every((outcome) => outcome === "unchanged")) {
|
|
2443
|
-
return "unchanged";
|
|
2444
|
-
}
|
|
2445
|
-
if (outcomes.includes("positive") && !outcomes.includes("negative")) {
|
|
2446
|
-
return "positive";
|
|
2447
|
-
}
|
|
2448
|
-
if (outcomes.includes("negative") && !outcomes.includes("positive")) {
|
|
2449
|
-
return "negative";
|
|
2450
|
-
}
|
|
2451
|
-
return "mixed";
|
|
2452
|
-
}
|
|
2453
|
-
function countDiffOutcomes(outcomes) {
|
|
2454
|
-
return {
|
|
2455
|
-
positive: outcomes.filter((outcome) => outcome === "positive").length,
|
|
2456
|
-
negative: outcomes.filter((outcome) => outcome === "negative").length,
|
|
2457
|
-
mixed: outcomes.filter((outcome) => outcome === "mixed").length,
|
|
2458
|
-
unchanged: outcomes.filter((outcome) => outcome === "unchanged").length
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2461
2641
|
|
|
2462
2642
|
// packages/utils/src/lib/reports/load-report.ts
|
|
2463
2643
|
import { join as join3 } from "node:path";
|
|
@@ -2514,7 +2694,8 @@ function logPlugins(report) {
|
|
|
2514
2694
|
},
|
|
2515
2695
|
{
|
|
2516
2696
|
text: cyanBright(audit.displayValue || `${audit.value}`),
|
|
2517
|
-
|
|
2697
|
+
// eslint-disable-next-line no-magic-numbers
|
|
2698
|
+
width: 20,
|
|
2518
2699
|
padding: [0, 0, 0, 0]
|
|
2519
2700
|
}
|
|
2520
2701
|
]);
|
|
@@ -2666,6 +2847,7 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2666
2847
|
});
|
|
2667
2848
|
export {
|
|
2668
2849
|
CODE_PUSHUP_DOMAIN,
|
|
2850
|
+
CODE_PUSHUP_UNICODE_LOGO,
|
|
2669
2851
|
FOOTER_PREFIX,
|
|
2670
2852
|
HIERARCHY,
|
|
2671
2853
|
NEW_LINE,
|
|
@@ -2693,9 +2875,11 @@ export {
|
|
|
2693
2875
|
formatBytes,
|
|
2694
2876
|
formatDuration,
|
|
2695
2877
|
formatGitPath,
|
|
2878
|
+
formatReportScore,
|
|
2696
2879
|
fromJsonLines,
|
|
2697
2880
|
generateMdReport,
|
|
2698
2881
|
generateMdReportsDiff,
|
|
2882
|
+
generateMdReportsDiffForMonorepo,
|
|
2699
2883
|
getCurrentBranchOrTag,
|
|
2700
2884
|
getGitRoot,
|
|
2701
2885
|
getHashFromTag,
|
|
@@ -2735,6 +2919,7 @@ export {
|
|
|
2735
2919
|
slugify,
|
|
2736
2920
|
sortReport,
|
|
2737
2921
|
sortSemvers,
|
|
2922
|
+
stringifyError,
|
|
2738
2923
|
toArray,
|
|
2739
2924
|
toGitPath,
|
|
2740
2925
|
toJsonLines,
|