@code-pushup/utils 0.45.0 → 0.46.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 +804 -797
- package/package.json +2 -2
- package/src/index.d.ts +6 -6
- package/src/lib/file-system.d.ts +1 -0
package/index.js
CHANGED
|
@@ -1,527 +1,158 @@
|
|
|
1
|
-
// packages/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var SPACE = " ";
|
|
5
|
-
|
|
6
|
-
// packages/utils/src/lib/text-formats/html/details.ts
|
|
7
|
-
function details(title, content, cfg = { open: false }) {
|
|
8
|
-
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.
|
|
9
|
-
NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
|
|
10
|
-
// ⚠️ The blank line ensure Markdown in content is rendered correctly.
|
|
11
|
-
NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
|
|
12
|
-
NEW_LINE}`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// packages/utils/src/lib/text-formats/html/font-style.ts
|
|
16
|
-
var boldElement = "b";
|
|
17
|
-
function bold(text) {
|
|
18
|
-
return `<${boldElement}>${text}</${boldElement}>`;
|
|
19
|
-
}
|
|
20
|
-
var italicElement = "i";
|
|
21
|
-
function italic(text) {
|
|
22
|
-
return `<${italicElement}>${text}</${italicElement}>`;
|
|
23
|
-
}
|
|
24
|
-
var codeElement = "code";
|
|
25
|
-
function code(text) {
|
|
26
|
-
return `<${codeElement}>${text}</${codeElement}>`;
|
|
27
|
-
}
|
|
1
|
+
// packages/models/src/lib/implementation/schemas.ts
|
|
2
|
+
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
3
|
+
import { z } from "zod";
|
|
28
4
|
|
|
29
|
-
// packages/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
5
|
+
// packages/models/src/lib/implementation/limits.ts
|
|
6
|
+
var MAX_SLUG_LENGTH = 128;
|
|
7
|
+
var MAX_TITLE_LENGTH = 256;
|
|
8
|
+
var MAX_DESCRIPTION_LENGTH = 65536;
|
|
9
|
+
var MAX_ISSUE_MESSAGE_LENGTH = 1024;
|
|
33
10
|
|
|
34
|
-
// packages/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
function objectToEntries(obj) {
|
|
43
|
-
return Object.entries(obj);
|
|
44
|
-
}
|
|
45
|
-
function objectFromEntries(entries) {
|
|
46
|
-
return Object.fromEntries(entries);
|
|
47
|
-
}
|
|
48
|
-
function countOccurrences(values) {
|
|
49
|
-
return values.reduce(
|
|
50
|
-
(acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
|
|
51
|
-
{}
|
|
11
|
+
// packages/models/src/lib/implementation/utils.ts
|
|
12
|
+
var slugRegex = /^[a-z\d]+(?:-[a-z\d]+)*$/;
|
|
13
|
+
var filenameRegex = /^(?!.*[ \\/:*?"<>|]).+$/;
|
|
14
|
+
function hasDuplicateStrings(strings) {
|
|
15
|
+
const sortedStrings = [...strings].sort();
|
|
16
|
+
const duplStrings = sortedStrings.filter(
|
|
17
|
+
(item, index) => index !== 0 && item === sortedStrings[index - 1]
|
|
52
18
|
);
|
|
19
|
+
return duplStrings.length === 0 ? false : [...new Set(duplStrings)];
|
|
53
20
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
21
|
+
function hasMissingStrings(toCheck, existing) {
|
|
22
|
+
const nonExisting = toCheck.filter((s) => !existing.includes(s));
|
|
23
|
+
return nonExisting.length === 0 ? false : nonExisting;
|
|
56
24
|
}
|
|
57
|
-
function
|
|
58
|
-
return
|
|
25
|
+
function errorItems(items, transform = (itemArr) => itemArr.join(", ")) {
|
|
26
|
+
return transform(items || []);
|
|
59
27
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
if (!itemCount) {
|
|
63
|
-
return 1;
|
|
64
|
-
}
|
|
65
|
-
const filterCount = items.filter(filterFn).length;
|
|
66
|
-
return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
|
|
28
|
+
function exists(value) {
|
|
29
|
+
return value != null;
|
|
67
30
|
}
|
|
68
|
-
function
|
|
69
|
-
if (
|
|
70
|
-
return
|
|
31
|
+
function getMissingRefsForCategories(categories, plugins) {
|
|
32
|
+
if (categories.length === 0) {
|
|
33
|
+
return false;
|
|
71
34
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
function toUnixPath(path) {
|
|
96
|
-
return path.replace(/\\/g, "/");
|
|
35
|
+
const auditRefsFromCategory = categories.flatMap(
|
|
36
|
+
({ refs }) => refs.filter(({ type }) => type === "audit").map(({ plugin, slug }) => `${plugin}/${slug}`)
|
|
37
|
+
);
|
|
38
|
+
const auditRefsFromPlugins = plugins.flatMap(
|
|
39
|
+
({ audits, slug: pluginSlug }) => audits.map(({ slug }) => `${pluginSlug}/${slug}`)
|
|
40
|
+
);
|
|
41
|
+
const missingAuditRefs = hasMissingStrings(
|
|
42
|
+
auditRefsFromCategory,
|
|
43
|
+
auditRefsFromPlugins
|
|
44
|
+
);
|
|
45
|
+
const groupRefsFromCategory = categories.flatMap(
|
|
46
|
+
({ refs }) => refs.filter(({ type }) => type === "group").map(({ plugin, slug }) => `${plugin}#${slug} (group)`)
|
|
47
|
+
);
|
|
48
|
+
const groupRefsFromPlugins = plugins.flatMap(
|
|
49
|
+
({ groups, slug: pluginSlug }) => Array.isArray(groups) ? groups.map(({ slug }) => `${pluginSlug}#${slug} (group)`) : []
|
|
50
|
+
);
|
|
51
|
+
const missingGroupRefs = hasMissingStrings(
|
|
52
|
+
groupRefsFromCategory,
|
|
53
|
+
groupRefsFromPlugins
|
|
54
|
+
);
|
|
55
|
+
const missingRefs = [missingAuditRefs, missingGroupRefs].filter((refs) => Array.isArray(refs) && refs.length > 0).flat();
|
|
56
|
+
return missingRefs.length > 0 ? missingRefs : false;
|
|
97
57
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
58
|
+
function missingRefsForCategoriesErrorMsg(categories, plugins) {
|
|
59
|
+
const missingRefs = getMissingRefsForCategories(categories, plugins);
|
|
60
|
+
return `The following category references need to point to an audit or group: ${errorItems(
|
|
61
|
+
missingRefs
|
|
62
|
+
)}`;
|
|
100
63
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
64
|
+
|
|
65
|
+
// packages/models/src/lib/implementation/schemas.ts
|
|
66
|
+
var primitiveValueSchema = z.union([z.string(), z.number()]);
|
|
67
|
+
function executionMetaSchema(options = {
|
|
68
|
+
descriptionDate: "Execution start date and time",
|
|
69
|
+
descriptionDuration: "Execution duration in ms"
|
|
70
|
+
}) {
|
|
71
|
+
return z.object({
|
|
72
|
+
date: z.string({ description: options.descriptionDate }),
|
|
73
|
+
duration: z.number({ description: options.descriptionDuration })
|
|
74
|
+
});
|
|
104
75
|
}
|
|
105
|
-
|
|
106
|
-
|
|
76
|
+
var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
|
|
77
|
+
message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
|
|
78
|
+
}).max(MAX_SLUG_LENGTH, {
|
|
79
|
+
message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
|
|
80
|
+
});
|
|
81
|
+
var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
|
|
82
|
+
var urlSchema = z.string().url();
|
|
83
|
+
var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
|
|
84
|
+
var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
|
|
85
|
+
var scoreSchema = z.number({
|
|
86
|
+
description: "Value between 0 and 1"
|
|
87
|
+
}).min(0).max(1);
|
|
88
|
+
function metaSchema(options) {
|
|
89
|
+
const {
|
|
90
|
+
descriptionDescription,
|
|
91
|
+
titleDescription,
|
|
92
|
+
docsUrlDescription,
|
|
93
|
+
description
|
|
94
|
+
} = options ?? {};
|
|
95
|
+
return z.object(
|
|
96
|
+
{
|
|
97
|
+
title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
|
|
98
|
+
description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
|
|
99
|
+
docsUrl: docsUrlDescription ? docsUrlSchema.describe(docsUrlDescription) : docsUrlSchema
|
|
100
|
+
},
|
|
101
|
+
{ description }
|
|
102
|
+
);
|
|
107
103
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
|
|
105
|
+
var fileNameSchema = z.string().trim().regex(filenameRegex, {
|
|
106
|
+
message: `The filename has to be valid`
|
|
107
|
+
}).min(1, { message: "file name is invalid" });
|
|
108
|
+
var positiveIntSchema = z.number().int().positive();
|
|
109
|
+
var nonnegativeIntSchema = z.number().int().nonnegative();
|
|
110
|
+
var nonnegativeNumberSchema = z.number().nonnegative();
|
|
111
|
+
function packageVersionSchema(options) {
|
|
112
|
+
const { versionDescription = "NPM version of the package", required } = options ?? {};
|
|
113
|
+
const packageSchema = z.string({ description: "NPM package name" });
|
|
114
|
+
const versionSchema = z.string({ description: versionDescription });
|
|
115
|
+
return z.object(
|
|
116
|
+
{
|
|
117
|
+
packageName: required ? packageSchema : packageSchema.optional(),
|
|
118
|
+
version: required ? versionSchema : versionSchema.optional()
|
|
119
|
+
},
|
|
120
|
+
{ description: "NPM package name and version of a published package" }
|
|
121
|
+
);
|
|
112
122
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
var weightSchema = nonnegativeNumberSchema.describe(
|
|
124
|
+
"Coefficient for the given score (use weight 0 if only for display)"
|
|
125
|
+
);
|
|
126
|
+
function weightedRefSchema(description, slugDescription) {
|
|
127
|
+
return z.object(
|
|
128
|
+
{
|
|
129
|
+
slug: slugSchema.describe(slugDescription),
|
|
130
|
+
weight: weightSchema.describe("Weight used to calculate score")
|
|
131
|
+
},
|
|
132
|
+
{ description }
|
|
133
|
+
);
|
|
117
134
|
}
|
|
118
|
-
function
|
|
119
|
-
return
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
|
|
136
|
+
return z.object(
|
|
137
|
+
{
|
|
138
|
+
slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
|
|
139
|
+
refs: z.array(refSchema).min(1).refine(
|
|
140
|
+
(refs) => !duplicateCheckFn(refs),
|
|
141
|
+
(refs) => ({
|
|
142
|
+
message: duplicateMessageFn(refs)
|
|
143
|
+
})
|
|
144
|
+
).refine(hasNonZeroWeightedRef, () => ({
|
|
145
|
+
message: "In a category there has to be at least one ref with weight > 0"
|
|
146
|
+
}))
|
|
147
|
+
},
|
|
148
|
+
{ description }
|
|
123
149
|
);
|
|
124
150
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return `${value}nd`;
|
|
131
|
-
}
|
|
132
|
-
if (value % 10 === 3 && value % 100 !== 13) {
|
|
133
|
-
return `${value}rd`;
|
|
134
|
-
}
|
|
135
|
-
return `${value}th`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// packages/utils/src/lib/table.ts
|
|
139
|
-
function rowToStringArray({ rows, columns = [] }) {
|
|
140
|
-
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
141
|
-
throw new TypeError(
|
|
142
|
-
"Column can`t be object when rows are primitive values"
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return rows.map((row) => {
|
|
146
|
-
if (Array.isArray(row)) {
|
|
147
|
-
return row.map(String);
|
|
148
|
-
}
|
|
149
|
-
const objectRow = row;
|
|
150
|
-
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
151
|
-
return Object.values(objectRow).map(String);
|
|
152
|
-
}
|
|
153
|
-
return columns.map(
|
|
154
|
-
({ key }) => String(objectRow[key])
|
|
155
|
-
);
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
function columnsToStringArray({ rows, columns = [] }) {
|
|
159
|
-
const firstRow = rows.at(0);
|
|
160
|
-
const primitiveRows = Array.isArray(firstRow);
|
|
161
|
-
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
162
|
-
throw new Error("invalid union type. Caught by model parsing.");
|
|
163
|
-
}
|
|
164
|
-
if (columns.length === 0) {
|
|
165
|
-
if (Array.isArray(firstRow)) {
|
|
166
|
-
return firstRow.map((_, idx) => String(idx));
|
|
167
|
-
}
|
|
168
|
-
return Object.keys(firstRow);
|
|
169
|
-
}
|
|
170
|
-
if (typeof columns.at(0) === "string") {
|
|
171
|
-
return columns.map(String);
|
|
172
|
-
}
|
|
173
|
-
const cols = columns;
|
|
174
|
-
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
175
|
-
}
|
|
176
|
-
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
177
|
-
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
178
|
-
if (typeof column === "string") {
|
|
179
|
-
return column;
|
|
180
|
-
} else if (typeof column === "object") {
|
|
181
|
-
return column.align ?? "center";
|
|
182
|
-
} else {
|
|
183
|
-
return "center";
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
187
|
-
const column = columns.at(targetIdx);
|
|
188
|
-
if (column == null) {
|
|
189
|
-
return "center";
|
|
190
|
-
} else if (typeof column === "string") {
|
|
191
|
-
return column;
|
|
192
|
-
} else if (typeof column === "object") {
|
|
193
|
-
return column.align ?? "center";
|
|
194
|
-
} else {
|
|
195
|
-
return "center";
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
function getColumnAlignments({
|
|
199
|
-
rows,
|
|
200
|
-
columns = []
|
|
201
|
-
}) {
|
|
202
|
-
if (rows.at(0) == null) {
|
|
203
|
-
throw new Error("first row can`t be undefined.");
|
|
204
|
-
}
|
|
205
|
-
if (Array.isArray(rows.at(0))) {
|
|
206
|
-
const firstPrimitiveRow = rows.at(0);
|
|
207
|
-
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
208
|
-
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
const firstObject = rows.at(0);
|
|
212
|
-
return Object.keys(firstObject).map(
|
|
213
|
-
(key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// packages/utils/src/lib/text-formats/html/table.ts
|
|
218
|
-
function wrap(elem, content) {
|
|
219
|
-
return `<${elem}>${content}</${elem}>${NEW_LINE}`;
|
|
220
|
-
}
|
|
221
|
-
function wrapRow(content) {
|
|
222
|
-
const elem = "tr";
|
|
223
|
-
return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
|
|
224
|
-
}
|
|
225
|
-
function table(tableData) {
|
|
226
|
-
if (tableData.rows.length === 0) {
|
|
227
|
-
throw new Error("Data can't be empty");
|
|
228
|
-
}
|
|
229
|
-
const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
|
|
230
|
-
const tableHeaderRow = wrapRow(tableHeaderCols);
|
|
231
|
-
const tableBody = rowToStringArray(tableData).map((arr) => {
|
|
232
|
-
const columns = arr.map((s) => wrap("td", s)).join("");
|
|
233
|
-
return wrapRow(columns);
|
|
234
|
-
}).join("");
|
|
235
|
-
return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// packages/utils/src/lib/text-formats/md/font-style.ts
|
|
239
|
-
var boldWrap = "**";
|
|
240
|
-
function bold2(text) {
|
|
241
|
-
return `${boldWrap}${text}${boldWrap}`;
|
|
242
|
-
}
|
|
243
|
-
var italicWrap = "_";
|
|
244
|
-
function italic2(text) {
|
|
245
|
-
return `${italicWrap}${text}${italicWrap}`;
|
|
246
|
-
}
|
|
247
|
-
var strikeThroughWrap = "~";
|
|
248
|
-
function strikeThrough(text) {
|
|
249
|
-
return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
|
|
250
|
-
}
|
|
251
|
-
var codeWrap = "`";
|
|
252
|
-
function code2(text) {
|
|
253
|
-
return `${codeWrap}${text}${codeWrap}`;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// packages/utils/src/lib/text-formats/md/headline.ts
|
|
257
|
-
function headline(text, hierarchy = 1) {
|
|
258
|
-
return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
|
|
259
|
-
}
|
|
260
|
-
function h(text, hierarchy = 1) {
|
|
261
|
-
return headline(text, hierarchy);
|
|
262
|
-
}
|
|
263
|
-
function h1(text) {
|
|
264
|
-
return headline(text, 1);
|
|
265
|
-
}
|
|
266
|
-
function h2(text) {
|
|
267
|
-
return headline(text, 2);
|
|
268
|
-
}
|
|
269
|
-
function h3(text) {
|
|
270
|
-
return headline(text, 3);
|
|
271
|
-
}
|
|
272
|
-
function h4(text) {
|
|
273
|
-
return headline(text, 4);
|
|
274
|
-
}
|
|
275
|
-
function h5(text) {
|
|
276
|
-
return headline(text, 5);
|
|
277
|
-
}
|
|
278
|
-
function h6(text) {
|
|
279
|
-
return headline(text, 6);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// packages/utils/src/lib/text-formats/md/image.ts
|
|
283
|
-
function image(src, alt) {
|
|
284
|
-
return ``;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// packages/utils/src/lib/text-formats/md/link.ts
|
|
288
|
-
function link2(href, text) {
|
|
289
|
-
return `[${text || href}](${href})`;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// packages/utils/src/lib/text-formats/md/list.ts
|
|
293
|
-
function li(text, order = "unordered") {
|
|
294
|
-
const style = order === "unordered" ? "-" : "- [ ]";
|
|
295
|
-
return `${style} ${text}`;
|
|
296
|
-
}
|
|
297
|
-
function indentation(text, level = 1) {
|
|
298
|
-
return `${TAB.repeat(level)}${text}`;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// packages/utils/src/lib/text-formats/md/paragraphs.ts
|
|
302
|
-
function paragraphs(...sections) {
|
|
303
|
-
return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// packages/utils/src/lib/text-formats/md/section.ts
|
|
307
|
-
function section(...contents) {
|
|
308
|
-
return `${lines(...contents)}${NEW_LINE}`;
|
|
309
|
-
}
|
|
310
|
-
function lines(...contents) {
|
|
311
|
-
return `${contents.filter(Boolean).join(NEW_LINE)}`;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// packages/utils/src/lib/text-formats/md/table.ts
|
|
315
|
-
var alignString = /* @__PURE__ */ new Map([
|
|
316
|
-
["left", ":--"],
|
|
317
|
-
["center", ":--:"],
|
|
318
|
-
["right", "--:"]
|
|
319
|
-
]);
|
|
320
|
-
function tableRow(rows) {
|
|
321
|
-
return `|${rows.join("|")}|`;
|
|
322
|
-
}
|
|
323
|
-
function table2(data) {
|
|
324
|
-
if (data.rows.length === 0) {
|
|
325
|
-
throw new Error("Data can't be empty");
|
|
326
|
-
}
|
|
327
|
-
const alignmentRow = getColumnAlignments(data).map(
|
|
328
|
-
(s) => alignString.get(s) ?? String(alignString.get("center"))
|
|
329
|
-
);
|
|
330
|
-
return section(
|
|
331
|
-
`${lines(
|
|
332
|
-
tableRow(columnsToStringArray(data)),
|
|
333
|
-
tableRow(alignmentRow),
|
|
334
|
-
...rowToStringArray(data).map(tableRow)
|
|
335
|
-
)}`
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// packages/utils/src/lib/text-formats/index.ts
|
|
340
|
-
var md = {
|
|
341
|
-
bold: bold2,
|
|
342
|
-
italic: italic2,
|
|
343
|
-
strikeThrough,
|
|
344
|
-
code: code2,
|
|
345
|
-
link: link2,
|
|
346
|
-
image,
|
|
347
|
-
headline,
|
|
348
|
-
h,
|
|
349
|
-
h1,
|
|
350
|
-
h2,
|
|
351
|
-
h3,
|
|
352
|
-
h4,
|
|
353
|
-
h5,
|
|
354
|
-
h6,
|
|
355
|
-
indentation,
|
|
356
|
-
lines,
|
|
357
|
-
li,
|
|
358
|
-
section,
|
|
359
|
-
paragraphs,
|
|
360
|
-
table: table2
|
|
361
|
-
};
|
|
362
|
-
var html = {
|
|
363
|
-
bold,
|
|
364
|
-
italic,
|
|
365
|
-
code,
|
|
366
|
-
link,
|
|
367
|
-
details,
|
|
368
|
-
table
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
// packages/models/src/lib/implementation/schemas.ts
|
|
372
|
-
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
373
|
-
import { z } from "zod";
|
|
374
|
-
|
|
375
|
-
// packages/models/src/lib/implementation/limits.ts
|
|
376
|
-
var MAX_SLUG_LENGTH = 128;
|
|
377
|
-
var MAX_TITLE_LENGTH = 256;
|
|
378
|
-
var MAX_DESCRIPTION_LENGTH = 65536;
|
|
379
|
-
var MAX_ISSUE_MESSAGE_LENGTH = 1024;
|
|
380
|
-
|
|
381
|
-
// packages/models/src/lib/implementation/utils.ts
|
|
382
|
-
var slugRegex = /^[a-z\d]+(?:-[a-z\d]+)*$/;
|
|
383
|
-
var filenameRegex = /^(?!.*[ \\/:*?"<>|]).+$/;
|
|
384
|
-
function hasDuplicateStrings(strings) {
|
|
385
|
-
const sortedStrings = [...strings].sort();
|
|
386
|
-
const duplStrings = sortedStrings.filter(
|
|
387
|
-
(item, index) => index !== 0 && item === sortedStrings[index - 1]
|
|
388
|
-
);
|
|
389
|
-
return duplStrings.length === 0 ? false : [...new Set(duplStrings)];
|
|
390
|
-
}
|
|
391
|
-
function hasMissingStrings(toCheck, existing) {
|
|
392
|
-
const nonExisting = toCheck.filter((s) => !existing.includes(s));
|
|
393
|
-
return nonExisting.length === 0 ? false : nonExisting;
|
|
394
|
-
}
|
|
395
|
-
function errorItems(items, transform = (itemArr) => itemArr.join(", ")) {
|
|
396
|
-
return transform(items || []);
|
|
397
|
-
}
|
|
398
|
-
function exists(value) {
|
|
399
|
-
return value != null;
|
|
400
|
-
}
|
|
401
|
-
function getMissingRefsForCategories(categories, plugins) {
|
|
402
|
-
if (categories.length === 0) {
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
const auditRefsFromCategory = categories.flatMap(
|
|
406
|
-
({ refs }) => refs.filter(({ type }) => type === "audit").map(({ plugin, slug }) => `${plugin}/${slug}`)
|
|
407
|
-
);
|
|
408
|
-
const auditRefsFromPlugins = plugins.flatMap(
|
|
409
|
-
({ audits, slug: pluginSlug }) => audits.map(({ slug }) => `${pluginSlug}/${slug}`)
|
|
410
|
-
);
|
|
411
|
-
const missingAuditRefs = hasMissingStrings(
|
|
412
|
-
auditRefsFromCategory,
|
|
413
|
-
auditRefsFromPlugins
|
|
414
|
-
);
|
|
415
|
-
const groupRefsFromCategory = categories.flatMap(
|
|
416
|
-
({ refs }) => refs.filter(({ type }) => type === "group").map(({ plugin, slug }) => `${plugin}#${slug} (group)`)
|
|
417
|
-
);
|
|
418
|
-
const groupRefsFromPlugins = plugins.flatMap(
|
|
419
|
-
({ groups, slug: pluginSlug }) => Array.isArray(groups) ? groups.map(({ slug }) => `${pluginSlug}#${slug} (group)`) : []
|
|
420
|
-
);
|
|
421
|
-
const missingGroupRefs = hasMissingStrings(
|
|
422
|
-
groupRefsFromCategory,
|
|
423
|
-
groupRefsFromPlugins
|
|
424
|
-
);
|
|
425
|
-
const missingRefs = [missingAuditRefs, missingGroupRefs].filter((refs) => Array.isArray(refs) && refs.length > 0).flat();
|
|
426
|
-
return missingRefs.length > 0 ? missingRefs : false;
|
|
427
|
-
}
|
|
428
|
-
function missingRefsForCategoriesErrorMsg(categories, plugins) {
|
|
429
|
-
const missingRefs = getMissingRefsForCategories(categories, plugins);
|
|
430
|
-
return `The following category references need to point to an audit or group: ${errorItems(
|
|
431
|
-
missingRefs
|
|
432
|
-
)}`;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// packages/models/src/lib/implementation/schemas.ts
|
|
436
|
-
var primitiveValueSchema = z.union([z.string(), z.number()]);
|
|
437
|
-
function executionMetaSchema(options = {
|
|
438
|
-
descriptionDate: "Execution start date and time",
|
|
439
|
-
descriptionDuration: "Execution duration in ms"
|
|
440
|
-
}) {
|
|
441
|
-
return z.object({
|
|
442
|
-
date: z.string({ description: options.descriptionDate }),
|
|
443
|
-
duration: z.number({ description: options.descriptionDuration })
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
|
|
447
|
-
message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
|
|
448
|
-
}).max(MAX_SLUG_LENGTH, {
|
|
449
|
-
message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
|
|
450
|
-
});
|
|
451
|
-
var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
|
|
452
|
-
var urlSchema = z.string().url();
|
|
453
|
-
var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
|
|
454
|
-
var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
|
|
455
|
-
var scoreSchema = z.number({
|
|
456
|
-
description: "Value between 0 and 1"
|
|
457
|
-
}).min(0).max(1);
|
|
458
|
-
function metaSchema(options) {
|
|
459
|
-
const {
|
|
460
|
-
descriptionDescription,
|
|
461
|
-
titleDescription,
|
|
462
|
-
docsUrlDescription,
|
|
463
|
-
description
|
|
464
|
-
} = options ?? {};
|
|
465
|
-
return z.object(
|
|
466
|
-
{
|
|
467
|
-
title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
|
|
468
|
-
description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
|
|
469
|
-
docsUrl: docsUrlDescription ? docsUrlSchema.describe(docsUrlDescription) : docsUrlSchema
|
|
470
|
-
},
|
|
471
|
-
{ description }
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
|
|
475
|
-
var fileNameSchema = z.string().trim().regex(filenameRegex, {
|
|
476
|
-
message: `The filename has to be valid`
|
|
477
|
-
}).min(1, { message: "file name is invalid" });
|
|
478
|
-
var positiveIntSchema = z.number().int().positive();
|
|
479
|
-
var nonnegativeIntSchema = z.number().int().nonnegative();
|
|
480
|
-
function packageVersionSchema(options) {
|
|
481
|
-
const { versionDescription = "NPM version of the package", required } = options ?? {};
|
|
482
|
-
const packageSchema = z.string({ description: "NPM package name" });
|
|
483
|
-
const versionSchema = z.string({ description: versionDescription });
|
|
484
|
-
return z.object(
|
|
485
|
-
{
|
|
486
|
-
packageName: required ? packageSchema : packageSchema.optional(),
|
|
487
|
-
version: required ? versionSchema : versionSchema.optional()
|
|
488
|
-
},
|
|
489
|
-
{ description: "NPM package name and version of a published package" }
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
var weightSchema = nonnegativeIntSchema.describe(
|
|
493
|
-
"Coefficient for the given score (use weight 0 if only for display)"
|
|
494
|
-
);
|
|
495
|
-
function weightedRefSchema(description, slugDescription) {
|
|
496
|
-
return z.object(
|
|
497
|
-
{
|
|
498
|
-
slug: slugSchema.describe(slugDescription),
|
|
499
|
-
weight: weightSchema.describe("Weight used to calculate score")
|
|
500
|
-
},
|
|
501
|
-
{ description }
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
|
|
505
|
-
return z.object(
|
|
506
|
-
{
|
|
507
|
-
slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
|
|
508
|
-
refs: z.array(refSchema).min(1).refine(
|
|
509
|
-
(refs) => !duplicateCheckFn(refs),
|
|
510
|
-
(refs) => ({
|
|
511
|
-
message: duplicateMessageFn(refs)
|
|
512
|
-
})
|
|
513
|
-
).refine(hasNonZeroWeightedRef, () => ({
|
|
514
|
-
message: "In a category there has to be at least one ref with weight > 0"
|
|
515
|
-
}))
|
|
516
|
-
},
|
|
517
|
-
{ description }
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
var materialIconSchema = z.enum(MATERIAL_ICONS, {
|
|
521
|
-
description: "Icon from VSCode Material Icons extension"
|
|
522
|
-
});
|
|
523
|
-
function hasNonZeroWeightedRef(refs) {
|
|
524
|
-
return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
|
|
151
|
+
var materialIconSchema = z.enum(MATERIAL_ICONS, {
|
|
152
|
+
description: "Icon from VSCode Material Icons extension"
|
|
153
|
+
});
|
|
154
|
+
function hasNonZeroWeightedRef(refs) {
|
|
155
|
+
return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
|
|
525
156
|
}
|
|
526
157
|
|
|
527
158
|
// packages/models/src/lib/audit.ts
|
|
@@ -631,7 +262,7 @@ var tableObjectSchema = tableSharedSchema.merge(
|
|
|
631
262
|
var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
|
|
632
263
|
|
|
633
264
|
// packages/models/src/lib/audit-output.ts
|
|
634
|
-
var auditValueSchema =
|
|
265
|
+
var auditValueSchema = nonnegativeNumberSchema.describe("Raw numeric value");
|
|
635
266
|
var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
636
267
|
var auditDetailsSchema = z5.object(
|
|
637
268
|
{
|
|
@@ -1062,348 +693,723 @@ var reportsDiffSchema = z15.object({
|
|
|
1062
693
|
})
|
|
1063
694
|
);
|
|
1064
695
|
|
|
1065
|
-
// packages/utils/src/lib/diff.ts
|
|
1066
|
-
function matchArrayItemsByKey({
|
|
1067
|
-
before,
|
|
1068
|
-
after,
|
|
1069
|
-
key
|
|
1070
|
-
}) {
|
|
1071
|
-
const pairs = [];
|
|
1072
|
-
const added = [];
|
|
1073
|
-
const afterKeys = /* @__PURE__ */ new Set();
|
|
1074
|
-
const keyFn = typeof key === "function" ? key : (item) => item[key];
|
|
1075
|
-
for (const afterItem of after) {
|
|
1076
|
-
const afterKey = keyFn(afterItem);
|
|
1077
|
-
afterKeys.add(afterKey);
|
|
1078
|
-
const match = before.find((beforeItem) => keyFn(beforeItem) === afterKey);
|
|
1079
|
-
if (match) {
|
|
1080
|
-
pairs.push({ before: match, after: afterItem });
|
|
1081
|
-
} else {
|
|
1082
|
-
added.push(afterItem);
|
|
1083
|
-
}
|
|
696
|
+
// packages/utils/src/lib/diff.ts
|
|
697
|
+
function matchArrayItemsByKey({
|
|
698
|
+
before,
|
|
699
|
+
after,
|
|
700
|
+
key
|
|
701
|
+
}) {
|
|
702
|
+
const pairs = [];
|
|
703
|
+
const added = [];
|
|
704
|
+
const afterKeys = /* @__PURE__ */ new Set();
|
|
705
|
+
const keyFn = typeof key === "function" ? key : (item) => item[key];
|
|
706
|
+
for (const afterItem of after) {
|
|
707
|
+
const afterKey = keyFn(afterItem);
|
|
708
|
+
afterKeys.add(afterKey);
|
|
709
|
+
const match = before.find((beforeItem) => keyFn(beforeItem) === afterKey);
|
|
710
|
+
if (match) {
|
|
711
|
+
pairs.push({ before: match, after: afterItem });
|
|
712
|
+
} else {
|
|
713
|
+
added.push(afterItem);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const removed = before.filter(
|
|
717
|
+
(beforeItem) => !afterKeys.has(keyFn(beforeItem))
|
|
718
|
+
);
|
|
719
|
+
return {
|
|
720
|
+
pairs,
|
|
721
|
+
added,
|
|
722
|
+
removed
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function comparePairs(pairs, equalsFn) {
|
|
726
|
+
return pairs.reduce(
|
|
727
|
+
(acc, pair) => ({
|
|
728
|
+
...acc,
|
|
729
|
+
...equalsFn(pair) ? { unchanged: [...acc.unchanged, pair.after] } : { changed: [...acc.changed, pair] }
|
|
730
|
+
}),
|
|
731
|
+
{
|
|
732
|
+
changed: [],
|
|
733
|
+
unchanged: []
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// packages/utils/src/lib/execute-process.ts
|
|
739
|
+
import { spawn } from "node:child_process";
|
|
740
|
+
|
|
741
|
+
// packages/utils/src/lib/reports/utils.ts
|
|
742
|
+
import { join as join2 } from "node:path";
|
|
743
|
+
|
|
744
|
+
// packages/utils/src/lib/file-system.ts
|
|
745
|
+
import { bundleRequire } from "bundle-require";
|
|
746
|
+
import chalk2 from "chalk";
|
|
747
|
+
import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
748
|
+
import { join } from "node:path";
|
|
749
|
+
|
|
750
|
+
// packages/utils/src/lib/formatting.ts
|
|
751
|
+
function slugify(text) {
|
|
752
|
+
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
753
|
+
}
|
|
754
|
+
function pluralize(text, amount) {
|
|
755
|
+
if (amount != null && Math.abs(amount) === 1) {
|
|
756
|
+
return text;
|
|
757
|
+
}
|
|
758
|
+
if (text.endsWith("y")) {
|
|
759
|
+
return `${text.slice(0, -1)}ies`;
|
|
760
|
+
}
|
|
761
|
+
if (text.endsWith("s")) {
|
|
762
|
+
return `${text}es`;
|
|
763
|
+
}
|
|
764
|
+
return `${text}s`;
|
|
765
|
+
}
|
|
766
|
+
function formatBytes(bytes, decimals = 2) {
|
|
767
|
+
const positiveBytes = Math.max(bytes, 0);
|
|
768
|
+
if (positiveBytes === 0) {
|
|
769
|
+
return "0 B";
|
|
770
|
+
}
|
|
771
|
+
const k = 1024;
|
|
772
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
773
|
+
const sizes = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
774
|
+
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
775
|
+
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
776
|
+
}
|
|
777
|
+
function pluralizeToken(token, times) {
|
|
778
|
+
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
779
|
+
}
|
|
780
|
+
function formatDuration(duration) {
|
|
781
|
+
if (duration < 1e3) {
|
|
782
|
+
return `${duration} ms`;
|
|
783
|
+
}
|
|
784
|
+
return `${(duration / 1e3).toFixed(2)} s`;
|
|
785
|
+
}
|
|
786
|
+
function formatDate(date) {
|
|
787
|
+
const locale = "en-US";
|
|
788
|
+
return date.toLocaleString(locale, {
|
|
789
|
+
weekday: "short",
|
|
790
|
+
month: "short",
|
|
791
|
+
day: "numeric",
|
|
792
|
+
year: "numeric",
|
|
793
|
+
hour: "numeric",
|
|
794
|
+
minute: "2-digit",
|
|
795
|
+
timeZoneName: "short"
|
|
796
|
+
}).replace(/\u202F/g, " ");
|
|
797
|
+
}
|
|
798
|
+
function truncateText(text, maxChars) {
|
|
799
|
+
if (text.length <= maxChars) {
|
|
800
|
+
return text;
|
|
801
|
+
}
|
|
802
|
+
const ellipsis = "...";
|
|
803
|
+
return text.slice(0, maxChars - ellipsis.length) + ellipsis;
|
|
804
|
+
}
|
|
805
|
+
function truncateTitle(text) {
|
|
806
|
+
return truncateText(text, MAX_TITLE_LENGTH);
|
|
807
|
+
}
|
|
808
|
+
function truncateDescription(text) {
|
|
809
|
+
return truncateText(text, MAX_DESCRIPTION_LENGTH);
|
|
810
|
+
}
|
|
811
|
+
function truncateIssueMessage(text) {
|
|
812
|
+
return truncateText(text, MAX_ISSUE_MESSAGE_LENGTH);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// packages/utils/src/lib/guards.ts
|
|
816
|
+
function isPromiseFulfilledResult(result) {
|
|
817
|
+
return result.status === "fulfilled";
|
|
818
|
+
}
|
|
819
|
+
function isPromiseRejectedResult(result) {
|
|
820
|
+
return result.status === "rejected";
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// packages/utils/src/lib/logging.ts
|
|
824
|
+
import isaacs_cliui from "@isaacs/cliui";
|
|
825
|
+
import { cliui } from "@poppinss/cliui";
|
|
826
|
+
import chalk from "chalk";
|
|
827
|
+
|
|
828
|
+
// packages/utils/src/lib/reports/constants.ts
|
|
829
|
+
var TERMINAL_WIDTH = 80;
|
|
830
|
+
var SCORE_COLOR_RANGE = {
|
|
831
|
+
GREEN_MIN: 0.9,
|
|
832
|
+
YELLOW_MIN: 0.5
|
|
833
|
+
};
|
|
834
|
+
var CATEGORIES_TITLE = "\u{1F3F7} Categories";
|
|
835
|
+
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
836
|
+
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
837
|
+
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
838
|
+
var reportHeadlineText = "Code PushUp Report";
|
|
839
|
+
var reportOverviewTableHeaders = [
|
|
840
|
+
{
|
|
841
|
+
key: "category",
|
|
842
|
+
label: "\u{1F3F7} Category",
|
|
843
|
+
align: "left"
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
key: "score",
|
|
847
|
+
label: "\u2B50 Score"
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
key: "audits",
|
|
851
|
+
label: "\u{1F6E1} Audits"
|
|
852
|
+
}
|
|
853
|
+
];
|
|
854
|
+
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
855
|
+
var issuesTableHeadings = [
|
|
856
|
+
{
|
|
857
|
+
key: "severity",
|
|
858
|
+
label: "Severity"
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
key: "message",
|
|
862
|
+
label: "Message"
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
key: "file",
|
|
866
|
+
label: "Source file"
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
key: "line",
|
|
870
|
+
label: "Line(s)"
|
|
871
|
+
}
|
|
872
|
+
];
|
|
873
|
+
|
|
874
|
+
// packages/utils/src/lib/logging.ts
|
|
875
|
+
var singletonUiInstance;
|
|
876
|
+
function ui() {
|
|
877
|
+
if (singletonUiInstance === void 0) {
|
|
878
|
+
singletonUiInstance = cliui();
|
|
1084
879
|
}
|
|
1085
|
-
const removed = before.filter(
|
|
1086
|
-
(beforeItem) => !afterKeys.has(keyFn(beforeItem))
|
|
1087
|
-
);
|
|
1088
880
|
return {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
881
|
+
...singletonUiInstance,
|
|
882
|
+
row: (args) => {
|
|
883
|
+
logListItem(args);
|
|
884
|
+
}
|
|
1092
885
|
};
|
|
1093
886
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
887
|
+
var singletonisaacUi;
|
|
888
|
+
function logListItem(args) {
|
|
889
|
+
if (singletonisaacUi === void 0) {
|
|
890
|
+
singletonisaacUi = isaacs_cliui({ width: TERMINAL_WIDTH });
|
|
891
|
+
}
|
|
892
|
+
singletonisaacUi.div(...args);
|
|
893
|
+
const content = singletonisaacUi.toString();
|
|
894
|
+
singletonisaacUi.rows = [];
|
|
895
|
+
singletonUiInstance?.logger.log(content);
|
|
896
|
+
}
|
|
897
|
+
function link(text) {
|
|
898
|
+
return chalk.underline(chalk.blueBright(text));
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// packages/utils/src/lib/log-results.ts
|
|
902
|
+
function logMultipleResults(results, messagePrefix, succeededTransform, failedTransform) {
|
|
903
|
+
if (succeededTransform) {
|
|
904
|
+
const succeededResults = results.filter(isPromiseFulfilledResult);
|
|
905
|
+
logPromiseResults(
|
|
906
|
+
succeededResults,
|
|
907
|
+
`${messagePrefix} successfully: `,
|
|
908
|
+
succeededTransform
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
if (failedTransform) {
|
|
912
|
+
const failedResults = results.filter(isPromiseRejectedResult);
|
|
913
|
+
logPromiseResults(
|
|
914
|
+
failedResults,
|
|
915
|
+
`${messagePrefix} failed: `,
|
|
916
|
+
failedTransform
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function logPromiseResults(results, logMessage, getMsg) {
|
|
921
|
+
if (results.length > 0) {
|
|
922
|
+
const log2 = results[0]?.status === "fulfilled" ? (m) => {
|
|
923
|
+
ui().logger.success(m);
|
|
924
|
+
} : (m) => {
|
|
925
|
+
ui().logger.warning(m);
|
|
926
|
+
};
|
|
927
|
+
log2(logMessage);
|
|
928
|
+
results.forEach((result) => {
|
|
929
|
+
log2(getMsg(result));
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// packages/utils/src/lib/file-system.ts
|
|
935
|
+
async function readTextFile(path) {
|
|
936
|
+
const buffer = await readFile(path);
|
|
937
|
+
return buffer.toString();
|
|
938
|
+
}
|
|
939
|
+
async function readJsonFile(path) {
|
|
940
|
+
const text = await readTextFile(path);
|
|
941
|
+
return JSON.parse(text);
|
|
942
|
+
}
|
|
943
|
+
async function fileExists(path) {
|
|
944
|
+
try {
|
|
945
|
+
const stats = await stat(path);
|
|
946
|
+
return stats.isFile();
|
|
947
|
+
} catch {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
async function directoryExists(path) {
|
|
952
|
+
try {
|
|
953
|
+
const stats = await stat(path);
|
|
954
|
+
return stats.isDirectory();
|
|
955
|
+
} catch {
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
async function ensureDirectoryExists(baseDir) {
|
|
960
|
+
try {
|
|
961
|
+
await mkdir(baseDir, { recursive: true });
|
|
962
|
+
return;
|
|
963
|
+
} catch (error) {
|
|
964
|
+
ui().logger.info(error.message);
|
|
965
|
+
if (error.code !== "EEXIST") {
|
|
966
|
+
throw error;
|
|
1103
967
|
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
async function removeDirectoryIfExists(dir) {
|
|
971
|
+
if (await directoryExists(dir)) {
|
|
972
|
+
await rm(dir, { recursive: true, force: true });
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function logMultipleFileResults(fileResults, messagePrefix) {
|
|
976
|
+
const succeededTransform = (result) => {
|
|
977
|
+
const [fileName, size] = result.value;
|
|
978
|
+
const formattedSize = size ? ` (${chalk2.gray(formatBytes(size))})` : "";
|
|
979
|
+
return `- ${chalk2.bold(fileName)}${formattedSize}`;
|
|
980
|
+
};
|
|
981
|
+
const failedTransform = (result) => `- ${chalk2.bold(result.reason)}`;
|
|
982
|
+
logMultipleResults(
|
|
983
|
+
fileResults,
|
|
984
|
+
messagePrefix,
|
|
985
|
+
succeededTransform,
|
|
986
|
+
failedTransform
|
|
1104
987
|
);
|
|
1105
988
|
}
|
|
989
|
+
var NoExportError = class extends Error {
|
|
990
|
+
constructor(filepath) {
|
|
991
|
+
super(`No default export found in ${filepath}`);
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
async function importEsmModule(options) {
|
|
995
|
+
const { mod } = await bundleRequire({
|
|
996
|
+
format: "esm",
|
|
997
|
+
...options
|
|
998
|
+
});
|
|
999
|
+
if (!("default" in mod)) {
|
|
1000
|
+
throw new NoExportError(options.filepath);
|
|
1001
|
+
}
|
|
1002
|
+
return mod.default;
|
|
1003
|
+
}
|
|
1004
|
+
function pluginWorkDir(slug) {
|
|
1005
|
+
return join("node_modules", ".code-pushup", slug);
|
|
1006
|
+
}
|
|
1007
|
+
async function crawlFileSystem(options) {
|
|
1008
|
+
const {
|
|
1009
|
+
directory,
|
|
1010
|
+
pattern,
|
|
1011
|
+
fileTransform = (filePath) => filePath
|
|
1012
|
+
} = options;
|
|
1013
|
+
const files = await readdir(directory);
|
|
1014
|
+
const promises = files.map(async (file) => {
|
|
1015
|
+
const filePath = join(directory, file);
|
|
1016
|
+
const stats = await stat(filePath);
|
|
1017
|
+
if (stats.isDirectory()) {
|
|
1018
|
+
return crawlFileSystem({ directory: filePath, pattern, fileTransform });
|
|
1019
|
+
}
|
|
1020
|
+
if (stats.isFile() && (!pattern || new RegExp(pattern).test(file))) {
|
|
1021
|
+
return fileTransform(filePath);
|
|
1022
|
+
}
|
|
1023
|
+
return [];
|
|
1024
|
+
});
|
|
1025
|
+
const resultsNestedArray = await Promise.all(promises);
|
|
1026
|
+
return resultsNestedArray.flat();
|
|
1027
|
+
}
|
|
1028
|
+
function findLineNumberInText(content, pattern) {
|
|
1029
|
+
const lines6 = content.split(/\r?\n/);
|
|
1030
|
+
const lineNumber = lines6.findIndex((line) => line.includes(pattern)) + 1;
|
|
1031
|
+
return lineNumber === 0 ? null : lineNumber;
|
|
1032
|
+
}
|
|
1033
|
+
function filePathToCliArg(path) {
|
|
1034
|
+
return `"${path}"`;
|
|
1035
|
+
}
|
|
1106
1036
|
|
|
1107
|
-
// packages/utils/src/lib/
|
|
1108
|
-
|
|
1037
|
+
// packages/utils/src/lib/text-formats/constants.ts
|
|
1038
|
+
var NEW_LINE = "\n";
|
|
1039
|
+
var TAB = " ";
|
|
1040
|
+
var SPACE = " ";
|
|
1109
1041
|
|
|
1110
|
-
// packages/utils/src/lib/
|
|
1111
|
-
|
|
1042
|
+
// packages/utils/src/lib/text-formats/html/details.ts
|
|
1043
|
+
function details(title, content, cfg = { open: false }) {
|
|
1044
|
+
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.
|
|
1045
|
+
NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
|
|
1046
|
+
// ⚠️ The blank line ensure Markdown in content is rendered correctly.
|
|
1047
|
+
NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
|
|
1048
|
+
NEW_LINE}`;
|
|
1049
|
+
}
|
|
1112
1050
|
|
|
1113
|
-
// packages/utils/src/lib/
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1051
|
+
// packages/utils/src/lib/text-formats/html/font-style.ts
|
|
1052
|
+
var boldElement = "b";
|
|
1053
|
+
function bold(text) {
|
|
1054
|
+
return `<${boldElement}>${text}</${boldElement}>`;
|
|
1055
|
+
}
|
|
1056
|
+
var italicElement = "i";
|
|
1057
|
+
function italic(text) {
|
|
1058
|
+
return `<${italicElement}>${text}</${italicElement}>`;
|
|
1059
|
+
}
|
|
1060
|
+
var codeElement = "code";
|
|
1061
|
+
function code(text) {
|
|
1062
|
+
return `<${codeElement}>${text}</${codeElement}>`;
|
|
1063
|
+
}
|
|
1118
1064
|
|
|
1119
|
-
// packages/utils/src/lib/
|
|
1120
|
-
function
|
|
1121
|
-
return
|
|
1065
|
+
// packages/utils/src/lib/text-formats/html/link.ts
|
|
1066
|
+
function link2(href, text) {
|
|
1067
|
+
return `<a href="${href}">${text || href}"</a>`;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// packages/utils/src/lib/transform.ts
|
|
1071
|
+
import { platform } from "node:os";
|
|
1072
|
+
function toArray(val) {
|
|
1073
|
+
return Array.isArray(val) ? val : [val];
|
|
1074
|
+
}
|
|
1075
|
+
function objectToKeys(obj) {
|
|
1076
|
+
return Object.keys(obj);
|
|
1077
|
+
}
|
|
1078
|
+
function objectToEntries(obj) {
|
|
1079
|
+
return Object.entries(obj);
|
|
1080
|
+
}
|
|
1081
|
+
function objectFromEntries(entries) {
|
|
1082
|
+
return Object.fromEntries(entries);
|
|
1083
|
+
}
|
|
1084
|
+
function countOccurrences(values) {
|
|
1085
|
+
return values.reduce(
|
|
1086
|
+
(acc, value) => ({ ...acc, [value]: (acc[value] ?? 0) + 1 }),
|
|
1087
|
+
{}
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
function distinct(array) {
|
|
1091
|
+
return [...new Set(array)];
|
|
1122
1092
|
}
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
return text;
|
|
1126
|
-
}
|
|
1127
|
-
if (text.endsWith("y")) {
|
|
1128
|
-
return `${text.slice(0, -1)}ies`;
|
|
1129
|
-
}
|
|
1130
|
-
if (text.endsWith("s")) {
|
|
1131
|
-
return `${text}es`;
|
|
1132
|
-
}
|
|
1133
|
-
return `${text}s`;
|
|
1093
|
+
function deepClone(obj) {
|
|
1094
|
+
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
1134
1095
|
}
|
|
1135
|
-
function
|
|
1136
|
-
const
|
|
1137
|
-
if (
|
|
1138
|
-
return
|
|
1096
|
+
function factorOf(items, filterFn) {
|
|
1097
|
+
const itemCount = items.length;
|
|
1098
|
+
if (!itemCount) {
|
|
1099
|
+
return 1;
|
|
1139
1100
|
}
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
const sizes = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
1143
|
-
const i = Math.floor(Math.log(positiveBytes) / Math.log(k));
|
|
1144
|
-
return `${Number.parseFloat((positiveBytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
1145
|
-
}
|
|
1146
|
-
function pluralizeToken(token, times) {
|
|
1147
|
-
return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
|
|
1101
|
+
const filterCount = items.filter(filterFn).length;
|
|
1102
|
+
return filterCount === 0 ? 1 : (itemCount - filterCount) / itemCount;
|
|
1148
1103
|
}
|
|
1149
|
-
function
|
|
1150
|
-
if (
|
|
1151
|
-
return
|
|
1104
|
+
function objectToCliArgs(params) {
|
|
1105
|
+
if (!params) {
|
|
1106
|
+
return [];
|
|
1152
1107
|
}
|
|
1153
|
-
return
|
|
1108
|
+
return Object.entries(params).flatMap(([key, value]) => {
|
|
1109
|
+
if (key === "_") {
|
|
1110
|
+
return Array.isArray(value) ? value : [`${value}`];
|
|
1111
|
+
}
|
|
1112
|
+
const prefix = key.length === 1 ? "-" : "--";
|
|
1113
|
+
if (Array.isArray(value)) {
|
|
1114
|
+
return value.map((v) => `${prefix}${key}="${v}"`);
|
|
1115
|
+
}
|
|
1116
|
+
if (Array.isArray(value)) {
|
|
1117
|
+
return value.map((v) => `${prefix}${key}="${v}"`);
|
|
1118
|
+
}
|
|
1119
|
+
if (typeof value === "string") {
|
|
1120
|
+
return [`${prefix}${key}="${value}"`];
|
|
1121
|
+
}
|
|
1122
|
+
if (typeof value === "number") {
|
|
1123
|
+
return [`${prefix}${key}=${value}`];
|
|
1124
|
+
}
|
|
1125
|
+
if (typeof value === "boolean") {
|
|
1126
|
+
return [`${prefix}${value ? "" : "no-"}${key}`];
|
|
1127
|
+
}
|
|
1128
|
+
throw new Error(`Unsupported type ${typeof value} for key ${key}`);
|
|
1129
|
+
});
|
|
1154
1130
|
}
|
|
1155
|
-
function
|
|
1156
|
-
|
|
1157
|
-
return date.toLocaleString(locale, {
|
|
1158
|
-
weekday: "short",
|
|
1159
|
-
month: "short",
|
|
1160
|
-
day: "numeric",
|
|
1161
|
-
year: "numeric",
|
|
1162
|
-
hour: "numeric",
|
|
1163
|
-
minute: "2-digit",
|
|
1164
|
-
timeZoneName: "short"
|
|
1165
|
-
}).replace(/\u202F/g, " ");
|
|
1131
|
+
function toUnixPath(path) {
|
|
1132
|
+
return path.replace(/\\/g, "/");
|
|
1166
1133
|
}
|
|
1167
|
-
function
|
|
1168
|
-
|
|
1169
|
-
return text;
|
|
1170
|
-
}
|
|
1171
|
-
const ellipsis = "...";
|
|
1172
|
-
return text.slice(0, maxChars - ellipsis.length) + ellipsis;
|
|
1134
|
+
function toUnixNewlines(text) {
|
|
1135
|
+
return platform() === "win32" ? text.replace(/\r\n/g, "\n") : text;
|
|
1173
1136
|
}
|
|
1174
|
-
function
|
|
1175
|
-
|
|
1137
|
+
function fromJsonLines(jsonLines) {
|
|
1138
|
+
const unifiedNewLines = toUnixNewlines(jsonLines).trim();
|
|
1139
|
+
return JSON.parse(`[${unifiedNewLines.split("\n").join(",")}]`);
|
|
1176
1140
|
}
|
|
1177
|
-
function
|
|
1178
|
-
return
|
|
1141
|
+
function toJsonLines(json) {
|
|
1142
|
+
return json.map((item) => JSON.stringify(item)).join("\n");
|
|
1179
1143
|
}
|
|
1180
|
-
function
|
|
1181
|
-
return
|
|
1144
|
+
function capitalize(text) {
|
|
1145
|
+
return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
|
|
1146
|
+
1
|
|
1147
|
+
)}`;
|
|
1182
1148
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
return
|
|
1149
|
+
function apostrophize(text, upperCase) {
|
|
1150
|
+
const lastCharMatch = text.match(/(\w)\W*$/);
|
|
1151
|
+
const lastChar = lastCharMatch?.[1] ?? "";
|
|
1152
|
+
return `${text}'${lastChar.toLocaleLowerCase() === "s" ? "" : upperCase ? "S" : "s"}`;
|
|
1187
1153
|
}
|
|
1188
|
-
function
|
|
1189
|
-
return
|
|
1154
|
+
function toNumberPrecision(value, decimalPlaces) {
|
|
1155
|
+
return Number(
|
|
1156
|
+
`${Math.round(
|
|
1157
|
+
Number.parseFloat(`${value}e${decimalPlaces}`)
|
|
1158
|
+
)}e-${decimalPlaces}`
|
|
1159
|
+
);
|
|
1190
1160
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
import { cliui } from "@poppinss/cliui";
|
|
1195
|
-
import chalk from "chalk";
|
|
1196
|
-
|
|
1197
|
-
// packages/utils/src/lib/reports/constants.ts
|
|
1198
|
-
var TERMINAL_WIDTH = 80;
|
|
1199
|
-
var SCORE_COLOR_RANGE = {
|
|
1200
|
-
GREEN_MIN: 0.9,
|
|
1201
|
-
YELLOW_MIN: 0.5
|
|
1202
|
-
};
|
|
1203
|
-
var CATEGORIES_TITLE = "\u{1F3F7} Categories";
|
|
1204
|
-
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
1205
|
-
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
1206
|
-
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
1207
|
-
var reportHeadlineText = "Code PushUp Report";
|
|
1208
|
-
var reportOverviewTableHeaders = [
|
|
1209
|
-
{
|
|
1210
|
-
key: "category",
|
|
1211
|
-
label: "\u{1F3F7} Category",
|
|
1212
|
-
align: "left"
|
|
1213
|
-
},
|
|
1214
|
-
{
|
|
1215
|
-
key: "score",
|
|
1216
|
-
label: "\u2B50 Score"
|
|
1217
|
-
},
|
|
1218
|
-
{
|
|
1219
|
-
key: "audits",
|
|
1220
|
-
label: "\u{1F6E1} Audits"
|
|
1161
|
+
function toOrdinal(value) {
|
|
1162
|
+
if (value % 10 === 1 && value % 100 !== 11) {
|
|
1163
|
+
return `${value}st`;
|
|
1221
1164
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
var issuesTableHeadings = [
|
|
1225
|
-
{
|
|
1226
|
-
key: "severity",
|
|
1227
|
-
label: "Severity"
|
|
1228
|
-
},
|
|
1229
|
-
{
|
|
1230
|
-
key: "message",
|
|
1231
|
-
label: "Message"
|
|
1232
|
-
},
|
|
1233
|
-
{
|
|
1234
|
-
key: "file",
|
|
1235
|
-
label: "Source file"
|
|
1236
|
-
},
|
|
1237
|
-
{
|
|
1238
|
-
key: "line",
|
|
1239
|
-
label: "Line(s)"
|
|
1165
|
+
if (value % 10 === 2 && value % 100 !== 12) {
|
|
1166
|
+
return `${value}nd`;
|
|
1240
1167
|
}
|
|
1241
|
-
|
|
1168
|
+
if (value % 10 === 3 && value % 100 !== 13) {
|
|
1169
|
+
return `${value}rd`;
|
|
1170
|
+
}
|
|
1171
|
+
return `${value}th`;
|
|
1172
|
+
}
|
|
1242
1173
|
|
|
1243
|
-
// packages/utils/src/lib/
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1174
|
+
// packages/utils/src/lib/table.ts
|
|
1175
|
+
function rowToStringArray({ rows, columns = [] }) {
|
|
1176
|
+
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
1177
|
+
throw new TypeError(
|
|
1178
|
+
"Column can`t be object when rows are primitive values"
|
|
1179
|
+
);
|
|
1248
1180
|
}
|
|
1249
|
-
return {
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
logListItem(args);
|
|
1181
|
+
return rows.map((row) => {
|
|
1182
|
+
if (Array.isArray(row)) {
|
|
1183
|
+
return row.map(String);
|
|
1253
1184
|
}
|
|
1254
|
-
|
|
1185
|
+
const objectRow = row;
|
|
1186
|
+
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
1187
|
+
return Object.values(objectRow).map(String);
|
|
1188
|
+
}
|
|
1189
|
+
return columns.map(
|
|
1190
|
+
({ key }) => String(objectRow[key])
|
|
1191
|
+
);
|
|
1192
|
+
});
|
|
1255
1193
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1194
|
+
function columnsToStringArray({ rows, columns = [] }) {
|
|
1195
|
+
const firstRow = rows.at(0);
|
|
1196
|
+
const primitiveRows = Array.isArray(firstRow);
|
|
1197
|
+
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
1198
|
+
throw new Error("invalid union type. Caught by model parsing.");
|
|
1260
1199
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1200
|
+
if (columns.length === 0) {
|
|
1201
|
+
if (Array.isArray(firstRow)) {
|
|
1202
|
+
return firstRow.map((_, idx) => String(idx));
|
|
1203
|
+
}
|
|
1204
|
+
return Object.keys(firstRow);
|
|
1205
|
+
}
|
|
1206
|
+
if (typeof columns.at(0) === "string") {
|
|
1207
|
+
return columns.map(String);
|
|
1208
|
+
}
|
|
1209
|
+
const cols = columns;
|
|
1210
|
+
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
1265
1211
|
}
|
|
1266
|
-
function
|
|
1267
|
-
|
|
1212
|
+
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
1213
|
+
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
1214
|
+
if (typeof column === "string") {
|
|
1215
|
+
return column;
|
|
1216
|
+
} else if (typeof column === "object") {
|
|
1217
|
+
return column.align ?? "center";
|
|
1218
|
+
} else {
|
|
1219
|
+
return "center";
|
|
1220
|
+
}
|
|
1268
1221
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1222
|
+
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
1223
|
+
const column = columns.at(targetIdx);
|
|
1224
|
+
if (column == null) {
|
|
1225
|
+
return "center";
|
|
1226
|
+
} else if (typeof column === "string") {
|
|
1227
|
+
return column;
|
|
1228
|
+
} else if (typeof column === "object") {
|
|
1229
|
+
return column.align ?? "center";
|
|
1230
|
+
} else {
|
|
1231
|
+
return "center";
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
function getColumnAlignments({
|
|
1235
|
+
rows,
|
|
1236
|
+
columns = []
|
|
1237
|
+
}) {
|
|
1238
|
+
if (rows.at(0) == null) {
|
|
1239
|
+
throw new Error("first row can`t be undefined.");
|
|
1279
1240
|
}
|
|
1280
|
-
if (
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
`${messagePrefix} failed: `,
|
|
1285
|
-
failedTransform
|
|
1241
|
+
if (Array.isArray(rows.at(0))) {
|
|
1242
|
+
const firstPrimitiveRow = rows.at(0);
|
|
1243
|
+
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
1244
|
+
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
1286
1245
|
);
|
|
1287
1246
|
}
|
|
1247
|
+
const firstObject = rows.at(0);
|
|
1248
|
+
return Object.keys(firstObject).map(
|
|
1249
|
+
(key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
|
|
1250
|
+
);
|
|
1288
1251
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1252
|
+
|
|
1253
|
+
// packages/utils/src/lib/text-formats/html/table.ts
|
|
1254
|
+
function wrap(elem, content) {
|
|
1255
|
+
return `<${elem}>${content}</${elem}>${NEW_LINE}`;
|
|
1256
|
+
}
|
|
1257
|
+
function wrapRow(content) {
|
|
1258
|
+
const elem = "tr";
|
|
1259
|
+
return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
|
|
1260
|
+
}
|
|
1261
|
+
function table(tableData) {
|
|
1262
|
+
if (tableData.rows.length === 0) {
|
|
1263
|
+
throw new Error("Data can't be empty");
|
|
1300
1264
|
}
|
|
1265
|
+
const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
|
|
1266
|
+
const tableHeaderRow = wrapRow(tableHeaderCols);
|
|
1267
|
+
const tableBody = rowToStringArray(tableData).map((arr) => {
|
|
1268
|
+
const columns = arr.map((s) => wrap("td", s)).join("");
|
|
1269
|
+
return wrapRow(columns);
|
|
1270
|
+
}).join("");
|
|
1271
|
+
return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
|
|
1301
1272
|
}
|
|
1302
1273
|
|
|
1303
|
-
// packages/utils/src/lib/
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
return
|
|
1274
|
+
// packages/utils/src/lib/text-formats/md/font-style.ts
|
|
1275
|
+
var boldWrap = "**";
|
|
1276
|
+
function bold2(text) {
|
|
1277
|
+
return `${boldWrap}${text}${boldWrap}`;
|
|
1307
1278
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
return
|
|
1279
|
+
var italicWrap = "_";
|
|
1280
|
+
function italic2(text) {
|
|
1281
|
+
return `${italicWrap}${text}${italicWrap}`;
|
|
1311
1282
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
return stats.isFile();
|
|
1316
|
-
} catch {
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1283
|
+
var strikeThroughWrap = "~";
|
|
1284
|
+
function strikeThrough(text) {
|
|
1285
|
+
return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
|
|
1319
1286
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
return stats.isDirectory();
|
|
1324
|
-
} catch {
|
|
1325
|
-
return false;
|
|
1326
|
-
}
|
|
1287
|
+
var codeWrap = "`";
|
|
1288
|
+
function code2(text) {
|
|
1289
|
+
return `${codeWrap}${text}${codeWrap}`;
|
|
1327
1290
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
} catch (error) {
|
|
1333
|
-
ui().logger.info(error.message);
|
|
1334
|
-
if (error.code !== "EEXIST") {
|
|
1335
|
-
throw error;
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1291
|
+
|
|
1292
|
+
// packages/utils/src/lib/text-formats/md/headline.ts
|
|
1293
|
+
function headline(text, hierarchy = 1) {
|
|
1294
|
+
return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
|
|
1338
1295
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
await rm(dir, { recursive: true, force: true });
|
|
1342
|
-
}
|
|
1296
|
+
function h(text, hierarchy = 1) {
|
|
1297
|
+
return headline(text, hierarchy);
|
|
1343
1298
|
}
|
|
1344
|
-
function
|
|
1345
|
-
|
|
1346
|
-
const [fileName, size] = result.value;
|
|
1347
|
-
const formattedSize = size ? ` (${chalk2.gray(formatBytes(size))})` : "";
|
|
1348
|
-
return `- ${chalk2.bold(fileName)}${formattedSize}`;
|
|
1349
|
-
};
|
|
1350
|
-
const failedTransform = (result) => `- ${chalk2.bold(result.reason)}`;
|
|
1351
|
-
logMultipleResults(
|
|
1352
|
-
fileResults,
|
|
1353
|
-
messagePrefix,
|
|
1354
|
-
succeededTransform,
|
|
1355
|
-
failedTransform
|
|
1356
|
-
);
|
|
1299
|
+
function h1(text) {
|
|
1300
|
+
return headline(text, 1);
|
|
1357
1301
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
super(`No default export found in ${filepath}`);
|
|
1361
|
-
}
|
|
1362
|
-
};
|
|
1363
|
-
async function importEsmModule(options) {
|
|
1364
|
-
const { mod } = await bundleRequire({
|
|
1365
|
-
format: "esm",
|
|
1366
|
-
...options
|
|
1367
|
-
});
|
|
1368
|
-
if (!("default" in mod)) {
|
|
1369
|
-
throw new NoExportError(options.filepath);
|
|
1370
|
-
}
|
|
1371
|
-
return mod.default;
|
|
1302
|
+
function h2(text) {
|
|
1303
|
+
return headline(text, 2);
|
|
1372
1304
|
}
|
|
1373
|
-
function
|
|
1374
|
-
return
|
|
1305
|
+
function h3(text) {
|
|
1306
|
+
return headline(text, 3);
|
|
1375
1307
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
directory,
|
|
1379
|
-
pattern,
|
|
1380
|
-
fileTransform = (filePath) => filePath
|
|
1381
|
-
} = options;
|
|
1382
|
-
const files = await readdir(directory);
|
|
1383
|
-
const promises = files.map(async (file) => {
|
|
1384
|
-
const filePath = join(directory, file);
|
|
1385
|
-
const stats = await stat(filePath);
|
|
1386
|
-
if (stats.isDirectory()) {
|
|
1387
|
-
return crawlFileSystem({ directory: filePath, pattern, fileTransform });
|
|
1388
|
-
}
|
|
1389
|
-
if (stats.isFile() && (!pattern || new RegExp(pattern).test(file))) {
|
|
1390
|
-
return fileTransform(filePath);
|
|
1391
|
-
}
|
|
1392
|
-
return [];
|
|
1393
|
-
});
|
|
1394
|
-
const resultsNestedArray = await Promise.all(promises);
|
|
1395
|
-
return resultsNestedArray.flat();
|
|
1308
|
+
function h4(text) {
|
|
1309
|
+
return headline(text, 4);
|
|
1396
1310
|
}
|
|
1397
|
-
function
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1311
|
+
function h5(text) {
|
|
1312
|
+
return headline(text, 5);
|
|
1313
|
+
}
|
|
1314
|
+
function h6(text) {
|
|
1315
|
+
return headline(text, 6);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
// packages/utils/src/lib/text-formats/md/image.ts
|
|
1319
|
+
function image(src, alt) {
|
|
1320
|
+
return ``;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// packages/utils/src/lib/text-formats/md/link.ts
|
|
1324
|
+
function link3(href, text) {
|
|
1325
|
+
return `[${text || href}](${href})`;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// packages/utils/src/lib/text-formats/md/list.ts
|
|
1329
|
+
function li(text, order = "unordered") {
|
|
1330
|
+
const style = order === "unordered" ? "-" : "- [ ]";
|
|
1331
|
+
return `${style} ${text}`;
|
|
1332
|
+
}
|
|
1333
|
+
function indentation(text, level = 1) {
|
|
1334
|
+
return `${TAB.repeat(level)}${text}`;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
// packages/utils/src/lib/text-formats/md/paragraphs.ts
|
|
1338
|
+
function paragraphs(...sections) {
|
|
1339
|
+
return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// packages/utils/src/lib/text-formats/md/section.ts
|
|
1343
|
+
function section(...contents) {
|
|
1344
|
+
return `${lines(...contents)}${NEW_LINE}`;
|
|
1345
|
+
}
|
|
1346
|
+
function lines(...contents) {
|
|
1347
|
+
return `${contents.filter(Boolean).join(NEW_LINE)}`;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// packages/utils/src/lib/text-formats/md/table.ts
|
|
1351
|
+
var alignString = /* @__PURE__ */ new Map([
|
|
1352
|
+
["left", ":--"],
|
|
1353
|
+
["center", ":--:"],
|
|
1354
|
+
["right", "--:"]
|
|
1355
|
+
]);
|
|
1356
|
+
function tableRow(rows) {
|
|
1357
|
+
return `|${rows.join("|")}|`;
|
|
1358
|
+
}
|
|
1359
|
+
function table2(data) {
|
|
1360
|
+
if (data.rows.length === 0) {
|
|
1361
|
+
throw new Error("Data can't be empty");
|
|
1362
|
+
}
|
|
1363
|
+
const alignmentRow = getColumnAlignments(data).map(
|
|
1364
|
+
(s) => alignString.get(s) ?? String(alignString.get("center"))
|
|
1365
|
+
);
|
|
1366
|
+
return section(
|
|
1367
|
+
`${lines(
|
|
1368
|
+
tableRow(columnsToStringArray(data)),
|
|
1369
|
+
tableRow(alignmentRow),
|
|
1370
|
+
...rowToStringArray(data).map(tableRow)
|
|
1371
|
+
)}`
|
|
1372
|
+
);
|
|
1401
1373
|
}
|
|
1402
1374
|
|
|
1375
|
+
// packages/utils/src/lib/text-formats/index.ts
|
|
1376
|
+
var md = {
|
|
1377
|
+
bold: bold2,
|
|
1378
|
+
italic: italic2,
|
|
1379
|
+
strikeThrough,
|
|
1380
|
+
code: code2,
|
|
1381
|
+
link: link3,
|
|
1382
|
+
image,
|
|
1383
|
+
headline,
|
|
1384
|
+
h,
|
|
1385
|
+
h1,
|
|
1386
|
+
h2,
|
|
1387
|
+
h3,
|
|
1388
|
+
h4,
|
|
1389
|
+
h5,
|
|
1390
|
+
h6,
|
|
1391
|
+
indentation,
|
|
1392
|
+
lines,
|
|
1393
|
+
li,
|
|
1394
|
+
section,
|
|
1395
|
+
paragraphs,
|
|
1396
|
+
table: table2
|
|
1397
|
+
};
|
|
1398
|
+
var html = {
|
|
1399
|
+
bold,
|
|
1400
|
+
italic,
|
|
1401
|
+
code,
|
|
1402
|
+
link: link2,
|
|
1403
|
+
details,
|
|
1404
|
+
table
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1403
1407
|
// packages/utils/src/lib/reports/utils.ts
|
|
1404
1408
|
var { image: image2, bold: boldMd } = md;
|
|
1405
1409
|
function formatReportScore(score) {
|
|
1406
|
-
|
|
1410
|
+
const scaledScore = score * 100;
|
|
1411
|
+
const roundedScore = Math.round(scaledScore);
|
|
1412
|
+
return roundedScore === 100 && score !== 1 ? Math.floor(scaledScore).toString() : roundedScore.toString();
|
|
1407
1413
|
}
|
|
1408
1414
|
function formatScoreWithColor(score, options) {
|
|
1409
1415
|
const styledNumber = options?.skipBold ? formatReportScore(score) : boldMd(formatReportScore(score));
|
|
@@ -2716,6 +2722,7 @@ export {
|
|
|
2716
2722
|
exists,
|
|
2717
2723
|
factorOf,
|
|
2718
2724
|
fileExists,
|
|
2725
|
+
filePathToCliArg,
|
|
2719
2726
|
filterItemRefsBy,
|
|
2720
2727
|
findLineNumberInText,
|
|
2721
2728
|
formatBytes,
|
|
@@ -2738,7 +2745,7 @@ export {
|
|
|
2738
2745
|
isPromiseFulfilledResult,
|
|
2739
2746
|
isPromiseRejectedResult,
|
|
2740
2747
|
isSemver,
|
|
2741
|
-
|
|
2748
|
+
link,
|
|
2742
2749
|
listAuditsFromAllPlugins,
|
|
2743
2750
|
listGroupsFromAllPlugins,
|
|
2744
2751
|
loadReport,
|