@code-pushup/eslint-plugin 0.56.0 → 0.57.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/package.json +8 -7
- package/src/bin.js +3 -0
- package/src/bin.js.map +1 -0
- package/src/index.d.ts +3 -3
- package/src/index.js +4 -0
- package/src/index.js.map +1 -0
- package/src/lib/config.js +19 -0
- package/src/lib/config.js.map +1 -0
- package/src/lib/eslint-plugin.d.ts +1 -1
- package/src/lib/eslint-plugin.js +45 -0
- package/src/lib/eslint-plugin.js.map +1 -0
- package/src/lib/meta/groups.d.ts +1 -1
- package/src/lib/meta/groups.js +64 -0
- package/src/lib/meta/groups.js.map +1 -0
- package/src/lib/meta/hash.d.ts +2 -0
- package/src/lib/meta/hash.js +19 -0
- package/src/lib/meta/hash.js.map +1 -0
- package/src/lib/meta/index.d.ts +3 -2
- package/src/lib/meta/index.js +15 -0
- package/src/lib/meta/index.js.map +1 -0
- package/src/lib/meta/parse.d.ts +2 -1
- package/src/lib/meta/parse.js +36 -0
- package/src/lib/meta/parse.js.map +1 -0
- package/src/lib/meta/rules.d.ts +2 -2
- package/src/lib/meta/rules.js +22 -0
- package/src/lib/meta/rules.js.map +1 -0
- package/src/lib/meta/transform.d.ts +2 -2
- package/src/lib/meta/transform.js +21 -0
- package/src/lib/meta/transform.js.map +1 -0
- package/src/lib/meta/versions/detect.d.ts +1 -1
- package/src/lib/meta/versions/detect.js +22 -0
- package/src/lib/meta/versions/detect.js.map +1 -0
- package/src/lib/meta/versions/flat.d.ts +2 -2
- package/src/lib/meta/versions/flat.js +81 -0
- package/src/lib/meta/versions/flat.js.map +1 -0
- package/src/lib/meta/versions/formats.js +2 -0
- package/src/lib/meta/versions/formats.js.map +1 -0
- package/src/lib/meta/versions/index.d.ts +5 -5
- package/src/lib/meta/versions/index.js +12 -0
- package/src/lib/meta/versions/index.js.map +1 -0
- package/src/lib/meta/versions/legacy.d.ts +2 -2
- package/src/lib/meta/versions/legacy.js +36 -0
- package/src/lib/meta/versions/legacy.js.map +1 -0
- package/src/lib/nx/filter-project-graph.js +12 -0
- package/src/lib/nx/filter-project-graph.js.map +1 -0
- package/src/lib/nx/find-all-projects.d.ts +1 -1
- package/src/lib/nx/find-all-projects.js +40 -0
- package/src/lib/nx/find-all-projects.js.map +1 -0
- package/src/lib/nx/find-project-with-deps.d.ts +1 -1
- package/src/lib/nx/find-project-with-deps.js +35 -0
- package/src/lib/nx/find-project-with-deps.js.map +1 -0
- package/src/lib/nx/find-project-without-deps.d.ts +1 -1
- package/src/lib/nx/find-project-without-deps.js +36 -0
- package/src/lib/nx/find-project-without-deps.js.map +1 -0
- package/src/lib/nx/index.d.ts +3 -3
- package/src/lib/nx/index.js +4 -0
- package/src/lib/nx/index.js.map +1 -0
- package/src/lib/nx/projects-to-config.d.ts +1 -1
- package/src/lib/nx/projects-to-config.js +18 -0
- package/src/lib/nx/projects-to-config.js.map +1 -0
- package/src/lib/nx/traverse-graph.js +21 -0
- package/src/lib/nx/traverse-graph.js.map +1 -0
- package/src/lib/nx/utils.d.ts +1 -1
- package/src/lib/nx/utils.js +75 -0
- package/src/lib/nx/utils.js.map +1 -0
- package/src/lib/runner/index.d.ts +1 -1
- package/src/lib/runner/index.js +37 -0
- package/src/lib/runner/index.js.map +1 -0
- package/src/lib/runner/lint.d.ts +2 -2
- package/src/lib/runner/lint.js +49 -0
- package/src/lib/runner/lint.js.map +1 -0
- package/src/lib/runner/transform.d.ts +1 -1
- package/src/lib/runner/transform.js +74 -0
- package/src/lib/runner/transform.js.map +1 -0
- package/src/lib/runner/types.js +2 -0
- package/src/lib/runner/types.js.map +1 -0
- package/src/lib/setup.d.ts +1 -1
- package/src/lib/setup.js +17 -0
- package/src/lib/setup.js.map +1 -0
- package/bin.js +0 -1129
- package/index.js +0 -1413
package/index.js
DELETED
|
@@ -1,1413 +0,0 @@
|
|
|
1
|
-
// packages/plugin-eslint/src/lib/eslint-plugin.ts
|
|
2
|
-
import { dirname as dirname3, join as join4 } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
// packages/plugin-eslint/package.json
|
|
6
|
-
var name = "@code-pushup/eslint-plugin";
|
|
7
|
-
var version = "0.56.0";
|
|
8
|
-
|
|
9
|
-
// packages/plugin-eslint/src/lib/config.ts
|
|
10
|
-
import { z as z17 } from "zod";
|
|
11
|
-
|
|
12
|
-
// packages/models/src/lib/implementation/schemas.ts
|
|
13
|
-
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
|
|
16
|
-
// packages/models/src/lib/implementation/limits.ts
|
|
17
|
-
var MAX_SLUG_LENGTH = 128;
|
|
18
|
-
var MAX_TITLE_LENGTH = 256;
|
|
19
|
-
var MAX_DESCRIPTION_LENGTH = 65536;
|
|
20
|
-
var MAX_ISSUE_MESSAGE_LENGTH = 1024;
|
|
21
|
-
|
|
22
|
-
// packages/models/src/lib/implementation/utils.ts
|
|
23
|
-
var slugRegex = /^[a-z\d]+(?:-[a-z\d]+)*$/;
|
|
24
|
-
var filenameRegex = /^(?!.*[ \\/:*?"<>|]).+$/;
|
|
25
|
-
function hasDuplicateStrings(strings) {
|
|
26
|
-
const sortedStrings = strings.toSorted();
|
|
27
|
-
const duplStrings = sortedStrings.filter(
|
|
28
|
-
(item, index) => index !== 0 && item === sortedStrings[index - 1]
|
|
29
|
-
);
|
|
30
|
-
return duplStrings.length === 0 ? false : [...new Set(duplStrings)];
|
|
31
|
-
}
|
|
32
|
-
function hasMissingStrings(toCheck, existing) {
|
|
33
|
-
const nonExisting = toCheck.filter((s) => !existing.includes(s));
|
|
34
|
-
return nonExisting.length === 0 ? false : nonExisting;
|
|
35
|
-
}
|
|
36
|
-
function errorItems(items, transform = (itemArr) => itemArr.join(", ")) {
|
|
37
|
-
return transform(items || []);
|
|
38
|
-
}
|
|
39
|
-
function exists(value) {
|
|
40
|
-
return value != null;
|
|
41
|
-
}
|
|
42
|
-
function getMissingRefsForCategories(categories, plugins) {
|
|
43
|
-
if (!categories || categories.length === 0) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
const auditRefsFromCategory = categories.flatMap(
|
|
47
|
-
({ refs }) => refs.filter(({ type }) => type === "audit").map(({ plugin, slug }) => `${plugin}/${slug}`)
|
|
48
|
-
);
|
|
49
|
-
const auditRefsFromPlugins = plugins.flatMap(
|
|
50
|
-
({ audits, slug: pluginSlug }) => audits.map(({ slug }) => `${pluginSlug}/${slug}`)
|
|
51
|
-
);
|
|
52
|
-
const missingAuditRefs = hasMissingStrings(
|
|
53
|
-
auditRefsFromCategory,
|
|
54
|
-
auditRefsFromPlugins
|
|
55
|
-
);
|
|
56
|
-
const groupRefsFromCategory = categories.flatMap(
|
|
57
|
-
({ refs }) => refs.filter(({ type }) => type === "group").map(({ plugin, slug }) => `${plugin}#${slug} (group)`)
|
|
58
|
-
);
|
|
59
|
-
const groupRefsFromPlugins = plugins.flatMap(
|
|
60
|
-
({ groups, slug: pluginSlug }) => Array.isArray(groups) ? groups.map(({ slug }) => `${pluginSlug}#${slug} (group)`) : []
|
|
61
|
-
);
|
|
62
|
-
const missingGroupRefs = hasMissingStrings(
|
|
63
|
-
groupRefsFromCategory,
|
|
64
|
-
groupRefsFromPlugins
|
|
65
|
-
);
|
|
66
|
-
const missingRefs = [missingAuditRefs, missingGroupRefs].filter((refs) => Array.isArray(refs) && refs.length > 0).flat();
|
|
67
|
-
return missingRefs.length > 0 ? missingRefs : false;
|
|
68
|
-
}
|
|
69
|
-
function missingRefsForCategoriesErrorMsg(categories, plugins) {
|
|
70
|
-
const missingRefs = getMissingRefsForCategories(categories, plugins);
|
|
71
|
-
return `The following category references need to point to an audit or group: ${errorItems(
|
|
72
|
-
missingRefs
|
|
73
|
-
)}`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// packages/models/src/lib/implementation/schemas.ts
|
|
77
|
-
var tableCellValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]).default(null);
|
|
78
|
-
function executionMetaSchema(options = {
|
|
79
|
-
descriptionDate: "Execution start date and time",
|
|
80
|
-
descriptionDuration: "Execution duration in ms"
|
|
81
|
-
}) {
|
|
82
|
-
return z.object({
|
|
83
|
-
date: z.string({ description: options.descriptionDate }),
|
|
84
|
-
duration: z.number({ description: options.descriptionDuration })
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
|
|
88
|
-
message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
|
|
89
|
-
}).max(MAX_SLUG_LENGTH, {
|
|
90
|
-
message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
|
|
91
|
-
});
|
|
92
|
-
var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
|
|
93
|
-
var urlSchema = z.string().url();
|
|
94
|
-
var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
|
|
95
|
-
var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
|
|
96
|
-
var scoreSchema = z.number({
|
|
97
|
-
description: "Value between 0 and 1"
|
|
98
|
-
}).min(0).max(1);
|
|
99
|
-
function metaSchema(options) {
|
|
100
|
-
const {
|
|
101
|
-
descriptionDescription,
|
|
102
|
-
titleDescription,
|
|
103
|
-
docsUrlDescription,
|
|
104
|
-
description
|
|
105
|
-
} = options ?? {};
|
|
106
|
-
return z.object(
|
|
107
|
-
{
|
|
108
|
-
title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
|
|
109
|
-
description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
|
|
110
|
-
docsUrl: docsUrlDescription ? docsUrlSchema.describe(docsUrlDescription) : docsUrlSchema
|
|
111
|
-
},
|
|
112
|
-
{ description }
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
|
|
116
|
-
var fileNameSchema = z.string().trim().regex(filenameRegex, {
|
|
117
|
-
message: `The filename has to be valid`
|
|
118
|
-
}).min(1, { message: "file name is invalid" });
|
|
119
|
-
var positiveIntSchema = z.number().int().positive();
|
|
120
|
-
var nonnegativeNumberSchema = z.number().nonnegative();
|
|
121
|
-
function packageVersionSchema(options) {
|
|
122
|
-
const { versionDescription = "NPM version of the package", required } = options ?? {};
|
|
123
|
-
const packageSchema = z.string({ description: "NPM package name" });
|
|
124
|
-
const versionSchema = z.string({ description: versionDescription });
|
|
125
|
-
return z.object(
|
|
126
|
-
{
|
|
127
|
-
packageName: required ? packageSchema : packageSchema.optional(),
|
|
128
|
-
version: required ? versionSchema : versionSchema.optional()
|
|
129
|
-
},
|
|
130
|
-
{ description: "NPM package name and version of a published package" }
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
var weightSchema = nonnegativeNumberSchema.describe(
|
|
134
|
-
"Coefficient for the given score (use weight 0 if only for display)"
|
|
135
|
-
);
|
|
136
|
-
function weightedRefSchema(description, slugDescription) {
|
|
137
|
-
return z.object(
|
|
138
|
-
{
|
|
139
|
-
slug: slugSchema.describe(slugDescription),
|
|
140
|
-
weight: weightSchema.describe("Weight used to calculate score")
|
|
141
|
-
},
|
|
142
|
-
{ description }
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
|
|
146
|
-
return z.object(
|
|
147
|
-
{
|
|
148
|
-
slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
|
|
149
|
-
refs: z.array(refSchema).min(1).refine(
|
|
150
|
-
(refs) => !duplicateCheckFn(refs),
|
|
151
|
-
(refs) => ({
|
|
152
|
-
message: duplicateMessageFn(refs)
|
|
153
|
-
})
|
|
154
|
-
).refine(hasNonZeroWeightedRef, () => ({
|
|
155
|
-
message: "In a category there has to be at least one ref with weight > 0"
|
|
156
|
-
}))
|
|
157
|
-
},
|
|
158
|
-
{ description }
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
var materialIconSchema = z.enum(MATERIAL_ICONS, {
|
|
162
|
-
description: "Icon from VSCode Material Icons extension"
|
|
163
|
-
});
|
|
164
|
-
function hasNonZeroWeightedRef(refs) {
|
|
165
|
-
return refs.reduce((acc, { weight }) => weight + acc, 0) !== 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// packages/models/src/lib/source.ts
|
|
169
|
-
import { z as z2 } from "zod";
|
|
170
|
-
var sourceFileLocationSchema = z2.object(
|
|
171
|
-
{
|
|
172
|
-
file: filePathSchema.describe("Relative path to source file in Git repo"),
|
|
173
|
-
position: z2.object(
|
|
174
|
-
{
|
|
175
|
-
startLine: positiveIntSchema.describe("Start line"),
|
|
176
|
-
startColumn: positiveIntSchema.describe("Start column").optional(),
|
|
177
|
-
endLine: positiveIntSchema.describe("End line").optional(),
|
|
178
|
-
endColumn: positiveIntSchema.describe("End column").optional()
|
|
179
|
-
},
|
|
180
|
-
{ description: "Location in file" }
|
|
181
|
-
).optional()
|
|
182
|
-
},
|
|
183
|
-
{ description: "Source file location" }
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
// packages/models/src/lib/audit.ts
|
|
187
|
-
import { z as z3 } from "zod";
|
|
188
|
-
var auditSchema = z3.object({
|
|
189
|
-
slug: slugSchema.describe("ID (unique within plugin)")
|
|
190
|
-
}).merge(
|
|
191
|
-
metaSchema({
|
|
192
|
-
titleDescription: "Descriptive name",
|
|
193
|
-
descriptionDescription: "Description (markdown)",
|
|
194
|
-
docsUrlDescription: "Link to documentation (rationale)",
|
|
195
|
-
description: "List of scorable metrics for the given plugin"
|
|
196
|
-
})
|
|
197
|
-
);
|
|
198
|
-
var pluginAuditsSchema = z3.array(auditSchema, {
|
|
199
|
-
description: "List of audits maintained in a plugin"
|
|
200
|
-
}).min(1).refine(
|
|
201
|
-
(auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
|
|
202
|
-
(auditMetadata) => ({
|
|
203
|
-
message: duplicateSlugsInAuditsErrorMsg(auditMetadata)
|
|
204
|
-
})
|
|
205
|
-
);
|
|
206
|
-
function duplicateSlugsInAuditsErrorMsg(audits) {
|
|
207
|
-
const duplicateRefs = getDuplicateSlugsInAudits(audits);
|
|
208
|
-
return `In plugin audits the following slugs are not unique: ${errorItems(
|
|
209
|
-
duplicateRefs
|
|
210
|
-
)}`;
|
|
211
|
-
}
|
|
212
|
-
function getDuplicateSlugsInAudits(audits) {
|
|
213
|
-
return hasDuplicateStrings(audits.map(({ slug }) => slug));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// packages/models/src/lib/audit-output.ts
|
|
217
|
-
import { z as z6 } from "zod";
|
|
218
|
-
|
|
219
|
-
// packages/models/src/lib/issue.ts
|
|
220
|
-
import { z as z4 } from "zod";
|
|
221
|
-
var issueSeveritySchema = z4.enum(["info", "warning", "error"], {
|
|
222
|
-
description: "Severity level"
|
|
223
|
-
});
|
|
224
|
-
var issueSchema = z4.object(
|
|
225
|
-
{
|
|
226
|
-
message: z4.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
|
|
227
|
-
severity: issueSeveritySchema,
|
|
228
|
-
source: sourceFileLocationSchema.optional()
|
|
229
|
-
},
|
|
230
|
-
{ description: "Issue information" }
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// packages/models/src/lib/table.ts
|
|
234
|
-
import { z as z5 } from "zod";
|
|
235
|
-
var tableAlignmentSchema = z5.enum(["left", "center", "right"], {
|
|
236
|
-
description: "Cell alignment"
|
|
237
|
-
});
|
|
238
|
-
var tableColumnObjectSchema = z5.object({
|
|
239
|
-
key: z5.string(),
|
|
240
|
-
label: z5.string().optional(),
|
|
241
|
-
align: tableAlignmentSchema.optional()
|
|
242
|
-
});
|
|
243
|
-
var tableRowObjectSchema = z5.record(tableCellValueSchema, {
|
|
244
|
-
description: "Object row"
|
|
245
|
-
});
|
|
246
|
-
var tableRowPrimitiveSchema = z5.array(tableCellValueSchema, {
|
|
247
|
-
description: "Primitive row"
|
|
248
|
-
});
|
|
249
|
-
var tableSharedSchema = z5.object({
|
|
250
|
-
title: z5.string().optional().describe("Display title for table")
|
|
251
|
-
});
|
|
252
|
-
var tablePrimitiveSchema = tableSharedSchema.merge(
|
|
253
|
-
z5.object(
|
|
254
|
-
{
|
|
255
|
-
columns: z5.array(tableAlignmentSchema).optional(),
|
|
256
|
-
rows: z5.array(tableRowPrimitiveSchema)
|
|
257
|
-
},
|
|
258
|
-
{ description: "Table with primitive rows and optional alignment columns" }
|
|
259
|
-
)
|
|
260
|
-
);
|
|
261
|
-
var tableObjectSchema = tableSharedSchema.merge(
|
|
262
|
-
z5.object(
|
|
263
|
-
{
|
|
264
|
-
columns: z5.union([
|
|
265
|
-
z5.array(tableAlignmentSchema),
|
|
266
|
-
z5.array(tableColumnObjectSchema)
|
|
267
|
-
]).optional(),
|
|
268
|
-
rows: z5.array(tableRowObjectSchema)
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
description: "Table with object rows and optional alignment or object columns"
|
|
272
|
-
}
|
|
273
|
-
)
|
|
274
|
-
);
|
|
275
|
-
var tableSchema = (description = "Table information") => z5.union([tablePrimitiveSchema, tableObjectSchema], { description });
|
|
276
|
-
|
|
277
|
-
// packages/models/src/lib/audit-output.ts
|
|
278
|
-
var auditValueSchema = nonnegativeNumberSchema.describe("Raw numeric value");
|
|
279
|
-
var auditDisplayValueSchema = z6.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
|
|
280
|
-
var auditDetailsSchema = z6.object(
|
|
281
|
-
{
|
|
282
|
-
issues: z6.array(issueSchema, { description: "List of findings" }).optional(),
|
|
283
|
-
table: tableSchema("Table of related findings").optional()
|
|
284
|
-
},
|
|
285
|
-
{ description: "Detailed information" }
|
|
286
|
-
);
|
|
287
|
-
var auditOutputSchema = z6.object(
|
|
288
|
-
{
|
|
289
|
-
slug: slugSchema.describe("Reference to audit"),
|
|
290
|
-
displayValue: auditDisplayValueSchema,
|
|
291
|
-
value: auditValueSchema,
|
|
292
|
-
score: scoreSchema,
|
|
293
|
-
details: auditDetailsSchema.optional()
|
|
294
|
-
},
|
|
295
|
-
{ description: "Audit information" }
|
|
296
|
-
);
|
|
297
|
-
var auditOutputsSchema = z6.array(auditOutputSchema, {
|
|
298
|
-
description: "List of JSON formatted audit output emitted by the runner process of a plugin"
|
|
299
|
-
}).refine(
|
|
300
|
-
(audits) => !getDuplicateSlugsInAudits2(audits),
|
|
301
|
-
(audits) => ({ message: duplicateSlugsInAuditsErrorMsg2(audits) })
|
|
302
|
-
);
|
|
303
|
-
function duplicateSlugsInAuditsErrorMsg2(audits) {
|
|
304
|
-
const duplicateRefs = getDuplicateSlugsInAudits2(audits);
|
|
305
|
-
return `In plugin audits the slugs are not unique: ${errorItems(
|
|
306
|
-
duplicateRefs
|
|
307
|
-
)}`;
|
|
308
|
-
}
|
|
309
|
-
function getDuplicateSlugsInAudits2(audits) {
|
|
310
|
-
return hasDuplicateStrings(audits.map(({ slug }) => slug));
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// packages/models/src/lib/category-config.ts
|
|
314
|
-
import { z as z7 } from "zod";
|
|
315
|
-
var categoryRefSchema = weightedRefSchema(
|
|
316
|
-
"Weighted references to audits and/or groups for the category",
|
|
317
|
-
"Slug of an audit or group (depending on `type`)"
|
|
318
|
-
).merge(
|
|
319
|
-
z7.object({
|
|
320
|
-
type: z7.enum(["audit", "group"], {
|
|
321
|
-
description: "Discriminant for reference kind, affects where `slug` is looked up"
|
|
322
|
-
}),
|
|
323
|
-
plugin: slugSchema.describe(
|
|
324
|
-
"Plugin slug (plugin should contain referenced audit or group)"
|
|
325
|
-
)
|
|
326
|
-
})
|
|
327
|
-
);
|
|
328
|
-
var categoryConfigSchema = scorableSchema(
|
|
329
|
-
"Category with a score calculated from audits and groups from various plugins",
|
|
330
|
-
categoryRefSchema,
|
|
331
|
-
getDuplicateRefsInCategoryMetrics,
|
|
332
|
-
duplicateRefsInCategoryMetricsErrorMsg
|
|
333
|
-
).merge(
|
|
334
|
-
metaSchema({
|
|
335
|
-
titleDescription: "Category Title",
|
|
336
|
-
docsUrlDescription: "Category docs URL",
|
|
337
|
-
descriptionDescription: "Category description",
|
|
338
|
-
description: "Meta info for category"
|
|
339
|
-
})
|
|
340
|
-
).merge(
|
|
341
|
-
z7.object({
|
|
342
|
-
isBinary: z7.boolean({
|
|
343
|
-
description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
|
|
344
|
-
}).optional()
|
|
345
|
-
})
|
|
346
|
-
);
|
|
347
|
-
function duplicateRefsInCategoryMetricsErrorMsg(metrics) {
|
|
348
|
-
const duplicateRefs = getDuplicateRefsInCategoryMetrics(metrics);
|
|
349
|
-
return `In the categories, the following audit or group refs are duplicates: ${errorItems(
|
|
350
|
-
duplicateRefs
|
|
351
|
-
)}`;
|
|
352
|
-
}
|
|
353
|
-
function getDuplicateRefsInCategoryMetrics(metrics) {
|
|
354
|
-
return hasDuplicateStrings(
|
|
355
|
-
metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
var categoriesSchema = z7.array(categoryConfigSchema, {
|
|
359
|
-
description: "Categorization of individual audits"
|
|
360
|
-
}).refine(
|
|
361
|
-
(categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
|
|
362
|
-
(categoryCfg) => ({
|
|
363
|
-
message: duplicateSlugCategoriesErrorMsg(categoryCfg)
|
|
364
|
-
})
|
|
365
|
-
);
|
|
366
|
-
function duplicateSlugCategoriesErrorMsg(categories) {
|
|
367
|
-
const duplicateStringSlugs = getDuplicateSlugCategories(categories);
|
|
368
|
-
return `In the categories, the following slugs are duplicated: ${errorItems(
|
|
369
|
-
duplicateStringSlugs
|
|
370
|
-
)}`;
|
|
371
|
-
}
|
|
372
|
-
function getDuplicateSlugCategories(categories) {
|
|
373
|
-
return hasDuplicateStrings(categories.map(({ slug }) => slug));
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// packages/models/src/lib/commit.ts
|
|
377
|
-
import { z as z8 } from "zod";
|
|
378
|
-
var commitSchema = z8.object(
|
|
379
|
-
{
|
|
380
|
-
hash: z8.string({ description: "Commit SHA (full)" }).regex(
|
|
381
|
-
/^[\da-f]{40}$/,
|
|
382
|
-
"Commit SHA should be a 40-character hexadecimal string"
|
|
383
|
-
),
|
|
384
|
-
message: z8.string({ description: "Commit message" }),
|
|
385
|
-
date: z8.coerce.date({
|
|
386
|
-
description: "Date and time when commit was authored"
|
|
387
|
-
}),
|
|
388
|
-
author: z8.string({
|
|
389
|
-
description: "Commit author name"
|
|
390
|
-
}).trim()
|
|
391
|
-
},
|
|
392
|
-
{ description: "Git commit" }
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
// packages/models/src/lib/core-config.ts
|
|
396
|
-
import { z as z14 } from "zod";
|
|
397
|
-
|
|
398
|
-
// packages/models/src/lib/persist-config.ts
|
|
399
|
-
import { z as z9 } from "zod";
|
|
400
|
-
var formatSchema = z9.enum(["json", "md"]);
|
|
401
|
-
var persistConfigSchema = z9.object({
|
|
402
|
-
outputDir: filePathSchema.describe("Artifacts folder").optional(),
|
|
403
|
-
filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
|
|
404
|
-
format: z9.array(formatSchema).optional()
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// packages/models/src/lib/plugin-config.ts
|
|
408
|
-
import { z as z12 } from "zod";
|
|
409
|
-
|
|
410
|
-
// packages/models/src/lib/group.ts
|
|
411
|
-
import { z as z10 } from "zod";
|
|
412
|
-
var groupRefSchema = weightedRefSchema(
|
|
413
|
-
"Weighted reference to a group",
|
|
414
|
-
"Reference slug to a group within this plugin (e.g. 'max-lines')"
|
|
415
|
-
);
|
|
416
|
-
var groupMetaSchema = metaSchema({
|
|
417
|
-
titleDescription: "Descriptive name for the group",
|
|
418
|
-
descriptionDescription: "Description of the group (markdown)",
|
|
419
|
-
docsUrlDescription: "Group documentation site",
|
|
420
|
-
description: "Group metadata"
|
|
421
|
-
});
|
|
422
|
-
var groupSchema = scorableSchema(
|
|
423
|
-
'A group aggregates a set of audits into a single score which can be referenced from a category. E.g. the group slug "performance" groups audits and can be referenced in a category',
|
|
424
|
-
groupRefSchema,
|
|
425
|
-
getDuplicateRefsInGroups,
|
|
426
|
-
duplicateRefsInGroupsErrorMsg
|
|
427
|
-
).merge(groupMetaSchema);
|
|
428
|
-
var groupsSchema = z10.array(groupSchema, {
|
|
429
|
-
description: "List of groups"
|
|
430
|
-
}).optional().refine(
|
|
431
|
-
(groups) => !getDuplicateSlugsInGroups(groups),
|
|
432
|
-
(groups) => ({
|
|
433
|
-
message: duplicateSlugsInGroupsErrorMsg(groups)
|
|
434
|
-
})
|
|
435
|
-
);
|
|
436
|
-
function duplicateRefsInGroupsErrorMsg(groups) {
|
|
437
|
-
const duplicateRefs = getDuplicateRefsInGroups(groups);
|
|
438
|
-
return `In plugin groups the following references are not unique: ${errorItems(
|
|
439
|
-
duplicateRefs
|
|
440
|
-
)}`;
|
|
441
|
-
}
|
|
442
|
-
function getDuplicateRefsInGroups(groups) {
|
|
443
|
-
return hasDuplicateStrings(groups.map(({ slug: ref }) => ref).filter(exists));
|
|
444
|
-
}
|
|
445
|
-
function duplicateSlugsInGroupsErrorMsg(groups) {
|
|
446
|
-
const duplicateRefs = getDuplicateSlugsInGroups(groups);
|
|
447
|
-
return `In groups the following slugs are not unique: ${errorItems(
|
|
448
|
-
duplicateRefs
|
|
449
|
-
)}`;
|
|
450
|
-
}
|
|
451
|
-
function getDuplicateSlugsInGroups(groups) {
|
|
452
|
-
return Array.isArray(groups) ? hasDuplicateStrings(groups.map(({ slug }) => slug)) : false;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// packages/models/src/lib/runner-config.ts
|
|
456
|
-
import { z as z11 } from "zod";
|
|
457
|
-
var outputTransformSchema = z11.function().args(z11.unknown()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
|
|
458
|
-
var runnerConfigSchema = z11.object(
|
|
459
|
-
{
|
|
460
|
-
command: z11.string({
|
|
461
|
-
description: "Shell command to execute"
|
|
462
|
-
}),
|
|
463
|
-
args: z11.array(z11.string({ description: "Command arguments" })).optional(),
|
|
464
|
-
outputFile: filePathSchema.describe("Output path"),
|
|
465
|
-
outputTransform: outputTransformSchema.optional()
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
description: "How to execute runner"
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
var onProgressSchema = z11.function().args(z11.unknown()).returns(z11.void());
|
|
472
|
-
var runnerFunctionSchema = z11.function().args(onProgressSchema.optional()).returns(z11.union([auditOutputsSchema, z11.promise(auditOutputsSchema)]));
|
|
473
|
-
|
|
474
|
-
// packages/models/src/lib/plugin-config.ts
|
|
475
|
-
var pluginMetaSchema = packageVersionSchema().merge(
|
|
476
|
-
metaSchema({
|
|
477
|
-
titleDescription: "Descriptive name",
|
|
478
|
-
descriptionDescription: "Description (markdown)",
|
|
479
|
-
docsUrlDescription: "Plugin documentation site",
|
|
480
|
-
description: "Plugin metadata"
|
|
481
|
-
})
|
|
482
|
-
).merge(
|
|
483
|
-
z12.object({
|
|
484
|
-
slug: slugSchema.describe("Unique plugin slug within core config"),
|
|
485
|
-
icon: materialIconSchema
|
|
486
|
-
})
|
|
487
|
-
);
|
|
488
|
-
var pluginDataSchema = z12.object({
|
|
489
|
-
runner: z12.union([runnerConfigSchema, runnerFunctionSchema]),
|
|
490
|
-
audits: pluginAuditsSchema,
|
|
491
|
-
groups: groupsSchema
|
|
492
|
-
});
|
|
493
|
-
var pluginConfigSchema = pluginMetaSchema.merge(pluginDataSchema).refine(
|
|
494
|
-
(pluginCfg) => !getMissingRefsFromGroups(pluginCfg),
|
|
495
|
-
(pluginCfg) => ({
|
|
496
|
-
message: missingRefsFromGroupsErrorMsg(pluginCfg)
|
|
497
|
-
})
|
|
498
|
-
);
|
|
499
|
-
function missingRefsFromGroupsErrorMsg(pluginCfg) {
|
|
500
|
-
const missingRefs = getMissingRefsFromGroups(pluginCfg);
|
|
501
|
-
return `The following group references need to point to an existing audit in this plugin config: ${errorItems(
|
|
502
|
-
missingRefs
|
|
503
|
-
)}`;
|
|
504
|
-
}
|
|
505
|
-
function getMissingRefsFromGroups(pluginCfg) {
|
|
506
|
-
return hasMissingStrings(
|
|
507
|
-
pluginCfg.groups?.flatMap(
|
|
508
|
-
({ refs: audits }) => audits.map(({ slug: ref }) => ref)
|
|
509
|
-
) ?? [],
|
|
510
|
-
pluginCfg.audits.map(({ slug }) => slug)
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// packages/models/src/lib/upload-config.ts
|
|
515
|
-
import { z as z13 } from "zod";
|
|
516
|
-
var uploadConfigSchema = z13.object({
|
|
517
|
-
server: urlSchema.describe("URL of deployed portal API"),
|
|
518
|
-
apiKey: z13.string({
|
|
519
|
-
description: "API key with write access to portal (use `process.env` for security)"
|
|
520
|
-
}),
|
|
521
|
-
organization: slugSchema.describe(
|
|
522
|
-
"Organization slug from Code PushUp portal"
|
|
523
|
-
),
|
|
524
|
-
project: slugSchema.describe("Project slug from Code PushUp portal"),
|
|
525
|
-
timeout: z13.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// packages/models/src/lib/core-config.ts
|
|
529
|
-
var unrefinedCoreConfigSchema = z14.object({
|
|
530
|
-
plugins: z14.array(pluginConfigSchema, {
|
|
531
|
-
description: "List of plugins to be used (official, community-provided, or custom)"
|
|
532
|
-
}).min(1),
|
|
533
|
-
/** portal configuration for persisting results */
|
|
534
|
-
persist: persistConfigSchema.optional(),
|
|
535
|
-
/** portal configuration for uploading results */
|
|
536
|
-
upload: uploadConfigSchema.optional(),
|
|
537
|
-
categories: categoriesSchema.optional()
|
|
538
|
-
});
|
|
539
|
-
var coreConfigSchema = refineCoreConfig(unrefinedCoreConfigSchema);
|
|
540
|
-
function refineCoreConfig(schema) {
|
|
541
|
-
return schema.refine(
|
|
542
|
-
({ categories, plugins }) => !getMissingRefsForCategories(categories, plugins),
|
|
543
|
-
({ categories, plugins }) => ({
|
|
544
|
-
message: missingRefsForCategoriesErrorMsg(categories, plugins)
|
|
545
|
-
})
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// packages/models/src/lib/report.ts
|
|
550
|
-
import { z as z15 } from "zod";
|
|
551
|
-
var auditReportSchema = auditSchema.merge(auditOutputSchema);
|
|
552
|
-
var pluginReportSchema = pluginMetaSchema.merge(
|
|
553
|
-
executionMetaSchema({
|
|
554
|
-
descriptionDate: "Start date and time of plugin run",
|
|
555
|
-
descriptionDuration: "Duration of the plugin run in ms"
|
|
556
|
-
})
|
|
557
|
-
).merge(
|
|
558
|
-
z15.object({
|
|
559
|
-
audits: z15.array(auditReportSchema).min(1),
|
|
560
|
-
groups: z15.array(groupSchema).optional()
|
|
561
|
-
})
|
|
562
|
-
).refine(
|
|
563
|
-
(pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
|
|
564
|
-
(pluginReport) => ({
|
|
565
|
-
message: missingRefsFromGroupsErrorMsg2(
|
|
566
|
-
pluginReport.audits,
|
|
567
|
-
pluginReport.groups ?? []
|
|
568
|
-
)
|
|
569
|
-
})
|
|
570
|
-
);
|
|
571
|
-
function missingRefsFromGroupsErrorMsg2(audits, groups) {
|
|
572
|
-
const missingRefs = getMissingRefsFromGroups2(audits, groups);
|
|
573
|
-
return `group references need to point to an existing audit in this plugin report: ${errorItems(
|
|
574
|
-
missingRefs
|
|
575
|
-
)}`;
|
|
576
|
-
}
|
|
577
|
-
function getMissingRefsFromGroups2(audits, groups) {
|
|
578
|
-
return hasMissingStrings(
|
|
579
|
-
groups.flatMap(
|
|
580
|
-
({ refs: auditRefs }) => auditRefs.map(({ slug: ref }) => ref)
|
|
581
|
-
),
|
|
582
|
-
audits.map(({ slug }) => slug)
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
var reportSchema = packageVersionSchema({
|
|
586
|
-
versionDescription: "NPM version of the CLI",
|
|
587
|
-
required: true
|
|
588
|
-
}).merge(
|
|
589
|
-
executionMetaSchema({
|
|
590
|
-
descriptionDate: "Start date and time of the collect run",
|
|
591
|
-
descriptionDuration: "Duration of the collect run in ms"
|
|
592
|
-
})
|
|
593
|
-
).merge(
|
|
594
|
-
z15.object(
|
|
595
|
-
{
|
|
596
|
-
plugins: z15.array(pluginReportSchema).min(1),
|
|
597
|
-
categories: z15.array(categoryConfigSchema).optional(),
|
|
598
|
-
commit: commitSchema.describe("Git commit for which report was collected").nullable()
|
|
599
|
-
},
|
|
600
|
-
{ description: "Collect output data" }
|
|
601
|
-
)
|
|
602
|
-
).refine(
|
|
603
|
-
({ categories, plugins }) => !getMissingRefsForCategories(categories, plugins),
|
|
604
|
-
({ categories, plugins }) => ({
|
|
605
|
-
message: missingRefsForCategoriesErrorMsg(categories, plugins)
|
|
606
|
-
})
|
|
607
|
-
);
|
|
608
|
-
|
|
609
|
-
// packages/models/src/lib/reports-diff.ts
|
|
610
|
-
import { z as z16 } from "zod";
|
|
611
|
-
function makeComparisonSchema(schema) {
|
|
612
|
-
const sharedDescription = schema.description || "Result";
|
|
613
|
-
return z16.object({
|
|
614
|
-
before: schema.describe(`${sharedDescription} (source commit)`),
|
|
615
|
-
after: schema.describe(`${sharedDescription} (target commit)`)
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
|
|
619
|
-
return z16.object(
|
|
620
|
-
{
|
|
621
|
-
changed: z16.array(diffSchema),
|
|
622
|
-
unchanged: z16.array(resultSchema),
|
|
623
|
-
added: z16.array(resultSchema),
|
|
624
|
-
removed: z16.array(resultSchema)
|
|
625
|
-
},
|
|
626
|
-
{ description }
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
var scorableMetaSchema = z16.object({
|
|
630
|
-
slug: slugSchema,
|
|
631
|
-
title: titleSchema,
|
|
632
|
-
docsUrl: docsUrlSchema
|
|
633
|
-
});
|
|
634
|
-
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
635
|
-
z16.object({
|
|
636
|
-
plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
|
|
637
|
-
})
|
|
638
|
-
);
|
|
639
|
-
var scorableDiffSchema = scorableMetaSchema.merge(
|
|
640
|
-
z16.object({
|
|
641
|
-
scores: makeComparisonSchema(scoreSchema).merge(
|
|
642
|
-
z16.object({
|
|
643
|
-
diff: z16.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
|
|
644
|
-
})
|
|
645
|
-
).describe("Score comparison")
|
|
646
|
-
})
|
|
647
|
-
);
|
|
648
|
-
var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
|
|
649
|
-
scorableWithPluginMetaSchema
|
|
650
|
-
);
|
|
651
|
-
var categoryDiffSchema = scorableDiffSchema;
|
|
652
|
-
var groupDiffSchema = scorableWithPluginDiffSchema;
|
|
653
|
-
var auditDiffSchema = scorableWithPluginDiffSchema.merge(
|
|
654
|
-
z16.object({
|
|
655
|
-
values: makeComparisonSchema(auditValueSchema).merge(
|
|
656
|
-
z16.object({
|
|
657
|
-
diff: z16.number().describe("Value change (`values.after - values.before`)")
|
|
658
|
-
})
|
|
659
|
-
).describe("Audit `value` comparison"),
|
|
660
|
-
displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
|
|
661
|
-
"Audit `displayValue` comparison"
|
|
662
|
-
)
|
|
663
|
-
})
|
|
664
|
-
);
|
|
665
|
-
var categoryResultSchema = scorableMetaSchema.merge(
|
|
666
|
-
z16.object({ score: scoreSchema })
|
|
667
|
-
);
|
|
668
|
-
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
669
|
-
z16.object({ score: scoreSchema })
|
|
670
|
-
);
|
|
671
|
-
var auditResultSchema = scorableWithPluginMetaSchema.merge(
|
|
672
|
-
auditOutputSchema.pick({ score: true, value: true, displayValue: true })
|
|
673
|
-
);
|
|
674
|
-
var reportsDiffSchema = z16.object({
|
|
675
|
-
commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
|
|
676
|
-
portalUrl: urlSchema.optional().describe("Link to comparison page in Code PushUp portal"),
|
|
677
|
-
label: z16.string().optional().describe("Label (e.g. project name)"),
|
|
678
|
-
categories: makeArraysComparisonSchema(
|
|
679
|
-
categoryDiffSchema,
|
|
680
|
-
categoryResultSchema,
|
|
681
|
-
"Changes affecting categories"
|
|
682
|
-
),
|
|
683
|
-
groups: makeArraysComparisonSchema(
|
|
684
|
-
groupDiffSchema,
|
|
685
|
-
groupResultSchema,
|
|
686
|
-
"Changes affecting groups"
|
|
687
|
-
),
|
|
688
|
-
audits: makeArraysComparisonSchema(
|
|
689
|
-
auditDiffSchema,
|
|
690
|
-
auditResultSchema,
|
|
691
|
-
"Changes affecting audits"
|
|
692
|
-
)
|
|
693
|
-
}).merge(
|
|
694
|
-
packageVersionSchema({
|
|
695
|
-
versionDescription: "NPM version of the CLI (when `compare` was run)",
|
|
696
|
-
required: true
|
|
697
|
-
})
|
|
698
|
-
).merge(
|
|
699
|
-
executionMetaSchema({
|
|
700
|
-
descriptionDate: "Start date and time of the compare run",
|
|
701
|
-
descriptionDuration: "Duration of the compare run in ms"
|
|
702
|
-
})
|
|
703
|
-
);
|
|
704
|
-
|
|
705
|
-
// packages/utils/src/lib/reports/utils.ts
|
|
706
|
-
import ansis from "ansis";
|
|
707
|
-
import { md } from "build-md";
|
|
708
|
-
|
|
709
|
-
// packages/utils/src/lib/reports/constants.ts
|
|
710
|
-
var TERMINAL_WIDTH = 80;
|
|
711
|
-
|
|
712
|
-
// packages/utils/src/lib/file-system.ts
|
|
713
|
-
import { bold, gray } from "ansis";
|
|
714
|
-
import { bundleRequire } from "bundle-require";
|
|
715
|
-
import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
716
|
-
import { dirname, join } from "node:path";
|
|
717
|
-
|
|
718
|
-
// packages/utils/src/lib/formatting.ts
|
|
719
|
-
function slugify(text) {
|
|
720
|
-
return text.trim().toLowerCase().replace(/\s+|\//g, "-").replace(/[^a-z\d-]/g, "");
|
|
721
|
-
}
|
|
722
|
-
function truncateText(text, options) {
|
|
723
|
-
const {
|
|
724
|
-
maxChars,
|
|
725
|
-
position = "end",
|
|
726
|
-
ellipsis = "..."
|
|
727
|
-
} = typeof options === "number" ? { maxChars: options } : options;
|
|
728
|
-
if (text.length <= maxChars) {
|
|
729
|
-
return text;
|
|
730
|
-
}
|
|
731
|
-
const maxLength = maxChars - ellipsis.length;
|
|
732
|
-
switch (position) {
|
|
733
|
-
case "start":
|
|
734
|
-
return ellipsis + text.slice(-maxLength).trim();
|
|
735
|
-
case "middle":
|
|
736
|
-
const halfMaxChars = Math.floor(maxLength / 2);
|
|
737
|
-
return text.slice(0, halfMaxChars).trim() + ellipsis + text.slice(-halfMaxChars).trim();
|
|
738
|
-
case "end":
|
|
739
|
-
return text.slice(0, maxLength).trim() + ellipsis;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
function truncateTitle(text) {
|
|
743
|
-
return truncateText(text, MAX_TITLE_LENGTH);
|
|
744
|
-
}
|
|
745
|
-
function truncateDescription(text) {
|
|
746
|
-
return truncateText(text, MAX_DESCRIPTION_LENGTH);
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// packages/utils/src/lib/logging.ts
|
|
750
|
-
import isaacs_cliui from "@isaacs/cliui";
|
|
751
|
-
import { cliui } from "@poppinss/cliui";
|
|
752
|
-
import { underline } from "ansis";
|
|
753
|
-
var singletonUiInstance;
|
|
754
|
-
function ui() {
|
|
755
|
-
if (singletonUiInstance === void 0) {
|
|
756
|
-
singletonUiInstance = cliui();
|
|
757
|
-
}
|
|
758
|
-
return {
|
|
759
|
-
...singletonUiInstance,
|
|
760
|
-
row: (args) => {
|
|
761
|
-
logListItem(args);
|
|
762
|
-
}
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
var singletonisaacUi;
|
|
766
|
-
function logListItem(args) {
|
|
767
|
-
if (singletonisaacUi === void 0) {
|
|
768
|
-
singletonisaacUi = isaacs_cliui({ width: TERMINAL_WIDTH });
|
|
769
|
-
}
|
|
770
|
-
singletonisaacUi.div(...args);
|
|
771
|
-
const content = singletonisaacUi.toString();
|
|
772
|
-
singletonisaacUi.rows = [];
|
|
773
|
-
singletonUiInstance?.logger.log(content);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// packages/utils/src/lib/file-system.ts
|
|
777
|
-
async function fileExists(path) {
|
|
778
|
-
try {
|
|
779
|
-
const stats = await stat(path);
|
|
780
|
-
return stats.isFile();
|
|
781
|
-
} catch {
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
async function ensureDirectoryExists(baseDir) {
|
|
786
|
-
try {
|
|
787
|
-
await mkdir(baseDir, { recursive: true });
|
|
788
|
-
return;
|
|
789
|
-
} catch (error) {
|
|
790
|
-
ui().logger.info(error.message);
|
|
791
|
-
if (error.code !== "EEXIST") {
|
|
792
|
-
throw error;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
function pluginWorkDir(slug) {
|
|
797
|
-
return join("node_modules", ".code-pushup", slug);
|
|
798
|
-
}
|
|
799
|
-
async function findNearestFile(fileNames, cwd = process.cwd()) {
|
|
800
|
-
for (let directory = cwd; directory !== dirname(directory); directory = dirname(directory)) {
|
|
801
|
-
for (const file of fileNames) {
|
|
802
|
-
if (await fileExists(join(directory, file))) {
|
|
803
|
-
return join(directory, file);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
return void 0;
|
|
808
|
-
}
|
|
809
|
-
function filePathToCliArg(path) {
|
|
810
|
-
return `"${path}"`;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// packages/utils/src/lib/git/git.ts
|
|
814
|
-
import { simpleGit } from "simple-git";
|
|
815
|
-
|
|
816
|
-
// packages/utils/src/lib/transform.ts
|
|
817
|
-
function toArray(val) {
|
|
818
|
-
return Array.isArray(val) ? val : [val];
|
|
819
|
-
}
|
|
820
|
-
function objectToKeys(obj) {
|
|
821
|
-
return Object.keys(obj);
|
|
822
|
-
}
|
|
823
|
-
function distinct(array) {
|
|
824
|
-
return [...new Set(array)];
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// packages/utils/src/lib/git/git.commits-and-tags.ts
|
|
828
|
-
import { simpleGit as simpleGit2 } from "simple-git";
|
|
829
|
-
|
|
830
|
-
// packages/utils/src/lib/semver.ts
|
|
831
|
-
import { rcompare, valid } from "semver";
|
|
832
|
-
|
|
833
|
-
// packages/utils/src/lib/progress.ts
|
|
834
|
-
import { black, bold as bold2, gray as gray2, green } from "ansis";
|
|
835
|
-
import { MultiProgressBars } from "multi-progress-bars";
|
|
836
|
-
|
|
837
|
-
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
838
|
-
import { MarkdownDocument as MarkdownDocument3, md as md4 } from "build-md";
|
|
839
|
-
|
|
840
|
-
// packages/utils/src/lib/reports/formatting.ts
|
|
841
|
-
import {
|
|
842
|
-
MarkdownDocument,
|
|
843
|
-
md as md2
|
|
844
|
-
} from "build-md";
|
|
845
|
-
|
|
846
|
-
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
847
|
-
import { MarkdownDocument as MarkdownDocument2, md as md3 } from "build-md";
|
|
848
|
-
|
|
849
|
-
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
850
|
-
import {
|
|
851
|
-
MarkdownDocument as MarkdownDocument5,
|
|
852
|
-
md as md6
|
|
853
|
-
} from "build-md";
|
|
854
|
-
|
|
855
|
-
// packages/utils/src/lib/reports/generate-md-reports-diff-utils.ts
|
|
856
|
-
import { MarkdownDocument as MarkdownDocument4, md as md5 } from "build-md";
|
|
857
|
-
|
|
858
|
-
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
859
|
-
import { bold as bold4, cyan, cyanBright, green as green2, red } from "ansis";
|
|
860
|
-
|
|
861
|
-
// packages/plugin-eslint/src/lib/config.ts
|
|
862
|
-
var patternsSchema = z17.union([z17.string(), z17.array(z17.string()).min(1)], {
|
|
863
|
-
description: "Lint target files. May contain file paths, directory paths or glob patterns"
|
|
864
|
-
});
|
|
865
|
-
var eslintrcSchema = z17.string({ description: "Path to ESLint config file" });
|
|
866
|
-
var eslintTargetObjectSchema = z17.object({
|
|
867
|
-
eslintrc: eslintrcSchema.optional(),
|
|
868
|
-
patterns: patternsSchema
|
|
869
|
-
});
|
|
870
|
-
var eslintTargetSchema = z17.union([patternsSchema, eslintTargetObjectSchema]).transform(
|
|
871
|
-
(target) => typeof target === "string" || Array.isArray(target) ? { patterns: target } : target
|
|
872
|
-
);
|
|
873
|
-
var eslintPluginConfigSchema = z17.union([eslintTargetSchema, z17.array(eslintTargetSchema).min(1)]).transform(toArray);
|
|
874
|
-
|
|
875
|
-
// packages/plugin-eslint/src/lib/meta/hash.ts
|
|
876
|
-
import { createHash } from "node:crypto";
|
|
877
|
-
function ruleIdToSlug(ruleId, options) {
|
|
878
|
-
const slug = slugify(ruleId);
|
|
879
|
-
if (!options?.length) {
|
|
880
|
-
return slug;
|
|
881
|
-
}
|
|
882
|
-
return `${slug}-${jsonHash(options)}`;
|
|
883
|
-
}
|
|
884
|
-
function jsonHash(data, bytes = 8) {
|
|
885
|
-
return createHash("shake256", { outputLength: bytes }).update(JSON.stringify(data) || "null").digest("hex");
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// packages/plugin-eslint/src/lib/meta/parse.ts
|
|
889
|
-
function parseRuleId(ruleId) {
|
|
890
|
-
const i = ruleId.lastIndexOf("/");
|
|
891
|
-
if (i < 0) {
|
|
892
|
-
return { name: ruleId };
|
|
893
|
-
}
|
|
894
|
-
return {
|
|
895
|
-
plugin: ruleId.slice(0, i),
|
|
896
|
-
name: ruleId.slice(i + 1)
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
function isRuleOff(entry) {
|
|
900
|
-
const level = Array.isArray(entry) ? entry[0] : entry;
|
|
901
|
-
switch (level) {
|
|
902
|
-
case 0:
|
|
903
|
-
case "off":
|
|
904
|
-
return true;
|
|
905
|
-
case 1:
|
|
906
|
-
case 2:
|
|
907
|
-
case "warn":
|
|
908
|
-
case "error":
|
|
909
|
-
return false;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
function optionsFromRuleEntry(entry) {
|
|
913
|
-
return toArray(entry).slice(1);
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// packages/plugin-eslint/src/lib/meta/groups.ts
|
|
917
|
-
var typeGroups = {
|
|
918
|
-
problem: {
|
|
919
|
-
slug: "problems",
|
|
920
|
-
title: "Problems",
|
|
921
|
-
description: "Code that either will cause an error or may cause confusing behavior. Developers should consider this a high priority to resolve."
|
|
922
|
-
},
|
|
923
|
-
suggestion: {
|
|
924
|
-
slug: "suggestions",
|
|
925
|
-
title: "Suggestions",
|
|
926
|
-
description: "Something that could be done in a better way but no errors will occur if the code isn't changed."
|
|
927
|
-
},
|
|
928
|
-
layout: {
|
|
929
|
-
slug: "formatting",
|
|
930
|
-
title: "Formatting",
|
|
931
|
-
description: "Primarily about whitespace, semicolons, commas, and parentheses, all the parts of the program that determine how the code looks rather than how it executes."
|
|
932
|
-
}
|
|
933
|
-
};
|
|
934
|
-
function groupsFromRuleTypes(rules) {
|
|
935
|
-
const allTypes = objectToKeys(typeGroups);
|
|
936
|
-
const auditSlugsMap = rules.reduce(
|
|
937
|
-
(acc, { meta: { type }, ruleId, options }) => type == null ? acc : {
|
|
938
|
-
...acc,
|
|
939
|
-
[type]: [...acc[type] ?? [], ruleIdToSlug(ruleId, options)]
|
|
940
|
-
},
|
|
941
|
-
{}
|
|
942
|
-
);
|
|
943
|
-
return allTypes.map((type) => ({
|
|
944
|
-
...typeGroups[type],
|
|
945
|
-
refs: auditSlugsMap[type]?.map((slug) => ({ slug, weight: 1 })) ?? []
|
|
946
|
-
})).filter((group) => group.refs.length);
|
|
947
|
-
}
|
|
948
|
-
function groupsFromRuleCategories(rules) {
|
|
949
|
-
const categoriesMap = rules.reduce(
|
|
950
|
-
(acc, { meta: { docs }, ruleId, options }) => {
|
|
951
|
-
const category = docs?.category;
|
|
952
|
-
if (!category) {
|
|
953
|
-
return acc;
|
|
954
|
-
}
|
|
955
|
-
const { plugin = "" } = parseRuleId(ruleId);
|
|
956
|
-
return {
|
|
957
|
-
...acc,
|
|
958
|
-
[plugin]: {
|
|
959
|
-
...acc[plugin],
|
|
960
|
-
[category]: [
|
|
961
|
-
...acc[plugin]?.[category] ?? [],
|
|
962
|
-
ruleIdToSlug(ruleId, options)
|
|
963
|
-
]
|
|
964
|
-
}
|
|
965
|
-
};
|
|
966
|
-
},
|
|
967
|
-
{}
|
|
968
|
-
);
|
|
969
|
-
const groups = Object.entries(categoriesMap).flatMap(
|
|
970
|
-
([plugin, categories]) => Object.entries(categories).map(
|
|
971
|
-
([category, slugs]) => ({
|
|
972
|
-
slug: `${slugify(plugin)}-${slugify(category)}`,
|
|
973
|
-
title: `${category} (${plugin})`,
|
|
974
|
-
refs: slugs.map((slug) => ({ slug, weight: 1 }))
|
|
975
|
-
})
|
|
976
|
-
)
|
|
977
|
-
);
|
|
978
|
-
return groups.toSorted((a, b) => a.slug.localeCompare(b.slug));
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// packages/plugin-eslint/src/lib/meta/versions/flat.ts
|
|
982
|
-
import { builtinRules } from "eslint/use-at-your-own-risk";
|
|
983
|
-
import { isAbsolute, join as join2 } from "node:path";
|
|
984
|
-
import { pathToFileURL } from "node:url";
|
|
985
|
-
async function loadRulesForFlatConfig({
|
|
986
|
-
eslintrc
|
|
987
|
-
}) {
|
|
988
|
-
const config = eslintrc ? await loadConfigByPath(eslintrc) : await loadConfigByDefaultLocation();
|
|
989
|
-
const configs = toArray(config);
|
|
990
|
-
const rules = findEnabledRulesWithOptions(configs);
|
|
991
|
-
return rules.map((rule) => {
|
|
992
|
-
const meta = findRuleMeta(rule.ruleId, configs);
|
|
993
|
-
if (!meta) {
|
|
994
|
-
ui().logger.warning(`Cannot find metadata for rule ${rule.ruleId}`);
|
|
995
|
-
return null;
|
|
996
|
-
}
|
|
997
|
-
return { ...rule, meta };
|
|
998
|
-
}).filter(exists);
|
|
999
|
-
}
|
|
1000
|
-
async function loadConfigByDefaultLocation() {
|
|
1001
|
-
const flatConfigFileNames = [
|
|
1002
|
-
"eslint.config.js",
|
|
1003
|
-
"eslint.config.mjs",
|
|
1004
|
-
"eslint.config.cjs"
|
|
1005
|
-
];
|
|
1006
|
-
const configPath = await findNearestFile(flatConfigFileNames);
|
|
1007
|
-
if (configPath) {
|
|
1008
|
-
return loadConfigByPath(configPath);
|
|
1009
|
-
}
|
|
1010
|
-
throw new Error(
|
|
1011
|
-
[
|
|
1012
|
-
`ESLint config file not found - expected ${flatConfigFileNames.join("/")} in ${process.cwd()} or some parent directory`,
|
|
1013
|
-
"If your ESLint config is in a non-standard location, use the `eslintrc` parameter to specify the path."
|
|
1014
|
-
].join("\n")
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
async function loadConfigByPath(path) {
|
|
1018
|
-
const absolutePath = isAbsolute(path) ? path : join2(process.cwd(), path);
|
|
1019
|
-
const url = pathToFileURL(absolutePath).toString();
|
|
1020
|
-
const mod = await import(url);
|
|
1021
|
-
return "default" in mod ? mod.default : mod;
|
|
1022
|
-
}
|
|
1023
|
-
function findEnabledRulesWithOptions(configs) {
|
|
1024
|
-
const enabledRules = configs.flatMap(({ rules }) => Object.entries(rules ?? {})).filter(([, entry]) => entry != null && !isRuleOff(entry)).map(([ruleId, entry]) => ({
|
|
1025
|
-
ruleId,
|
|
1026
|
-
options: entry ? optionsFromRuleEntry(entry) : []
|
|
1027
|
-
}));
|
|
1028
|
-
const uniqueRulesMap = new Map(
|
|
1029
|
-
enabledRules.map(({ ruleId, options }) => [
|
|
1030
|
-
`${ruleId}::${jsonHash(options)}`,
|
|
1031
|
-
{ ruleId, options }
|
|
1032
|
-
])
|
|
1033
|
-
);
|
|
1034
|
-
return [...uniqueRulesMap.values()];
|
|
1035
|
-
}
|
|
1036
|
-
function findRuleMeta(ruleId, configs) {
|
|
1037
|
-
const { plugin, name: name2 } = parseRuleId(ruleId);
|
|
1038
|
-
if (!plugin) {
|
|
1039
|
-
return findBuiltinRuleMeta(name2);
|
|
1040
|
-
}
|
|
1041
|
-
return findPluginRuleMeta(plugin, name2, configs);
|
|
1042
|
-
}
|
|
1043
|
-
function findBuiltinRuleMeta(name2) {
|
|
1044
|
-
const rule = builtinRules.get(name2);
|
|
1045
|
-
return rule?.meta;
|
|
1046
|
-
}
|
|
1047
|
-
function findPluginRuleMeta(plugin, name2, configs) {
|
|
1048
|
-
const config = configs.find(({ plugins = {} }) => plugin in plugins);
|
|
1049
|
-
const rule = config?.plugins?.[plugin]?.rules?.[name2];
|
|
1050
|
-
if (typeof rule === "function") {
|
|
1051
|
-
ui().logger.warning(
|
|
1052
|
-
`Cannot parse metadata for rule ${plugin}/${name2}, plugin registers it as a function`
|
|
1053
|
-
);
|
|
1054
|
-
return void 0;
|
|
1055
|
-
}
|
|
1056
|
-
return rule?.meta;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// packages/plugin-eslint/src/lib/setup.ts
|
|
1060
|
-
import { ESLint } from "eslint";
|
|
1061
|
-
async function setupESLint(eslintrc) {
|
|
1062
|
-
const eslintConstructor = await loadESLint();
|
|
1063
|
-
return new eslintConstructor({
|
|
1064
|
-
overrideConfigFile: eslintrc,
|
|
1065
|
-
errorOnUnmatchedPattern: false
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
|
-
async function loadESLint() {
|
|
1069
|
-
const eslint = await import("eslint");
|
|
1070
|
-
if ("loadESLint" in eslint && typeof eslint.loadESLint === "function") {
|
|
1071
|
-
return await eslint.loadESLint();
|
|
1072
|
-
}
|
|
1073
|
-
return ESLint;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
// packages/plugin-eslint/src/lib/meta/versions/legacy.ts
|
|
1077
|
-
async function loadRulesForLegacyConfig({
|
|
1078
|
-
eslintrc,
|
|
1079
|
-
patterns
|
|
1080
|
-
}) {
|
|
1081
|
-
const eslint = await setupESLint(eslintrc);
|
|
1082
|
-
const configs = await toArray(patterns).reduce(
|
|
1083
|
-
async (acc, pattern) => [
|
|
1084
|
-
...await acc,
|
|
1085
|
-
await eslint.calculateConfigForFile(pattern)
|
|
1086
|
-
],
|
|
1087
|
-
Promise.resolve([])
|
|
1088
|
-
);
|
|
1089
|
-
const rulesIds = distinct(
|
|
1090
|
-
configs.flatMap((config) => Object.keys(config.rules ?? {}))
|
|
1091
|
-
);
|
|
1092
|
-
const rulesMeta = eslint.getRulesMetaForResults([
|
|
1093
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
1094
|
-
{
|
|
1095
|
-
messages: rulesIds.map((ruleId) => ({ ruleId })),
|
|
1096
|
-
suppressedMessages: []
|
|
1097
|
-
}
|
|
1098
|
-
]);
|
|
1099
|
-
return configs.flatMap((config) => Object.entries(config.rules ?? {})).map(([ruleId, ruleEntry]) => {
|
|
1100
|
-
if (ruleEntry == null || isRuleOff(ruleEntry)) {
|
|
1101
|
-
return null;
|
|
1102
|
-
}
|
|
1103
|
-
const meta = rulesMeta[ruleId];
|
|
1104
|
-
if (!meta) {
|
|
1105
|
-
ui().logger.warning(`Metadata not found for ESLint rule ${ruleId}`);
|
|
1106
|
-
return null;
|
|
1107
|
-
}
|
|
1108
|
-
const options = optionsFromRuleEntry(ruleEntry);
|
|
1109
|
-
return {
|
|
1110
|
-
ruleId,
|
|
1111
|
-
meta,
|
|
1112
|
-
options
|
|
1113
|
-
};
|
|
1114
|
-
}).filter(exists);
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
// packages/plugin-eslint/src/lib/meta/versions/detect.ts
|
|
1118
|
-
import { ESLint as ESLint2 } from "eslint";
|
|
1119
|
-
async function detectConfigVersion() {
|
|
1120
|
-
if (process.env["ESLINT_USE_FLAT_CONFIG"] === "true") {
|
|
1121
|
-
return "flat";
|
|
1122
|
-
}
|
|
1123
|
-
if (process.env["ESLINT_USE_FLAT_CONFIG"] === "false") {
|
|
1124
|
-
return "legacy";
|
|
1125
|
-
}
|
|
1126
|
-
if (ESLint2.version.startsWith("8.")) {
|
|
1127
|
-
if (await fileExists("eslint.config.js")) {
|
|
1128
|
-
return "flat";
|
|
1129
|
-
}
|
|
1130
|
-
return "legacy";
|
|
1131
|
-
}
|
|
1132
|
-
return "flat";
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
// packages/plugin-eslint/src/lib/meta/versions/index.ts
|
|
1136
|
-
function selectRulesLoader(version2) {
|
|
1137
|
-
switch (version2) {
|
|
1138
|
-
case "flat":
|
|
1139
|
-
return loadRulesForFlatConfig;
|
|
1140
|
-
case "legacy":
|
|
1141
|
-
return loadRulesForLegacyConfig;
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
// packages/plugin-eslint/src/lib/meta/rules.ts
|
|
1146
|
-
async function listRules(targets) {
|
|
1147
|
-
const version2 = await detectConfigVersion();
|
|
1148
|
-
const loadRulesMap = selectRulesLoader(version2);
|
|
1149
|
-
const rulesMap = await targets.reduce(async (acc, target) => {
|
|
1150
|
-
const map = await acc;
|
|
1151
|
-
const rules = await loadRulesMap(target);
|
|
1152
|
-
return rules.reduce(mergeRuleIntoMap, map);
|
|
1153
|
-
}, Promise.resolve({}));
|
|
1154
|
-
return Object.values(rulesMap).flatMap(Object.values);
|
|
1155
|
-
}
|
|
1156
|
-
function mergeRuleIntoMap(map, rule) {
|
|
1157
|
-
return {
|
|
1158
|
-
...map,
|
|
1159
|
-
[rule.ruleId]: {
|
|
1160
|
-
...map[rule.ruleId],
|
|
1161
|
-
[jsonHash(rule.options)]: rule
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
// packages/plugin-eslint/src/lib/meta/transform.ts
|
|
1167
|
-
function ruleToAudit({ ruleId, meta, options }) {
|
|
1168
|
-
const name2 = ruleId.split("/").at(-1) ?? ruleId;
|
|
1169
|
-
const plugin = name2 === ruleId ? null : ruleId.slice(0, ruleId.lastIndexOf("/"));
|
|
1170
|
-
const pluginContext = plugin ? `, from _${plugin}_ plugin` : "";
|
|
1171
|
-
const lines = [
|
|
1172
|
-
`ESLint rule **${name2}**${pluginContext}.`,
|
|
1173
|
-
...options?.length ? ["Custom options:"] : [],
|
|
1174
|
-
...options?.map(
|
|
1175
|
-
(option) => ["```json", JSON.stringify(option, null, 2), "```"].join("\n")
|
|
1176
|
-
) ?? []
|
|
1177
|
-
];
|
|
1178
|
-
return {
|
|
1179
|
-
slug: ruleIdToSlug(ruleId, options),
|
|
1180
|
-
title: truncateTitle(meta.docs?.description ?? name2),
|
|
1181
|
-
description: truncateDescription(lines.join("\n\n")),
|
|
1182
|
-
...meta.docs?.url && {
|
|
1183
|
-
docsUrl: meta.docs.url
|
|
1184
|
-
}
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
// packages/plugin-eslint/src/lib/meta/index.ts
|
|
1189
|
-
async function listAuditsAndGroups(targets) {
|
|
1190
|
-
const rules = await listRules(targets);
|
|
1191
|
-
const audits = rules.map(ruleToAudit);
|
|
1192
|
-
const groups = [
|
|
1193
|
-
...groupsFromRuleTypes(rules),
|
|
1194
|
-
...groupsFromRuleCategories(rules)
|
|
1195
|
-
];
|
|
1196
|
-
return { audits, groups };
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
// packages/plugin-eslint/src/lib/runner/index.ts
|
|
1200
|
-
import { writeFile } from "node:fs/promises";
|
|
1201
|
-
import { dirname as dirname2, join as join3 } from "node:path";
|
|
1202
|
-
var WORKDIR = pluginWorkDir("eslint");
|
|
1203
|
-
var RUNNER_OUTPUT_PATH = join3(WORKDIR, "runner-output.json");
|
|
1204
|
-
var PLUGIN_CONFIG_PATH = join3(
|
|
1205
|
-
process.cwd(),
|
|
1206
|
-
WORKDIR,
|
|
1207
|
-
"plugin-config.json"
|
|
1208
|
-
);
|
|
1209
|
-
async function createRunnerConfig(scriptPath, audits, targets) {
|
|
1210
|
-
const config = {
|
|
1211
|
-
targets,
|
|
1212
|
-
slugs: audits.map((audit) => audit.slug)
|
|
1213
|
-
};
|
|
1214
|
-
await ensureDirectoryExists(dirname2(PLUGIN_CONFIG_PATH));
|
|
1215
|
-
await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config));
|
|
1216
|
-
return {
|
|
1217
|
-
command: "node",
|
|
1218
|
-
args: [filePathToCliArg(scriptPath)],
|
|
1219
|
-
outputFile: RUNNER_OUTPUT_PATH
|
|
1220
|
-
};
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// packages/plugin-eslint/src/lib/eslint-plugin.ts
|
|
1224
|
-
async function eslintPlugin(config) {
|
|
1225
|
-
const targets = eslintPluginConfigSchema.parse(config);
|
|
1226
|
-
const { audits, groups } = await listAuditsAndGroups(targets);
|
|
1227
|
-
const runnerScriptPath = join4(
|
|
1228
|
-
fileURLToPath(dirname3(import.meta.url)),
|
|
1229
|
-
"bin.js"
|
|
1230
|
-
);
|
|
1231
|
-
return {
|
|
1232
|
-
slug: "eslint",
|
|
1233
|
-
title: "ESLint",
|
|
1234
|
-
icon: "eslint",
|
|
1235
|
-
description: "Official Code PushUp ESLint plugin",
|
|
1236
|
-
docsUrl: "https://www.npmjs.com/package/@code-pushup/eslint-plugin",
|
|
1237
|
-
packageName: name,
|
|
1238
|
-
version,
|
|
1239
|
-
audits,
|
|
1240
|
-
groups,
|
|
1241
|
-
runner: await createRunnerConfig(runnerScriptPath, audits, targets)
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// packages/plugin-eslint/src/lib/nx/filter-project-graph.ts
|
|
1246
|
-
function filterProjectGraph(projectGraph, exclude = []) {
|
|
1247
|
-
const filteredNodes = Object.entries(
|
|
1248
|
-
projectGraph.nodes
|
|
1249
|
-
).reduce(
|
|
1250
|
-
(acc, [projectName, projectNode]) => exclude.includes(projectName) ? acc : { ...acc, [projectName]: projectNode },
|
|
1251
|
-
{}
|
|
1252
|
-
);
|
|
1253
|
-
const filteredDependencies = Object.entries(projectGraph.dependencies).reduce(
|
|
1254
|
-
(acc, [key, deps]) => exclude.includes(key) ? acc : { ...acc, [key]: deps },
|
|
1255
|
-
{}
|
|
1256
|
-
);
|
|
1257
|
-
return {
|
|
1258
|
-
nodes: filteredNodes,
|
|
1259
|
-
dependencies: filteredDependencies,
|
|
1260
|
-
version: projectGraph.version
|
|
1261
|
-
};
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
// packages/plugin-eslint/src/lib/nx/utils.ts
|
|
1265
|
-
import { join as join5 } from "node:path";
|
|
1266
|
-
var ESLINT_CONFIG_EXTENSIONS = {
|
|
1267
|
-
// https://eslint.org/docs/latest/use/configure/configuration-files#configuration-file-formats
|
|
1268
|
-
flat: ["js", "mjs", "cjs"],
|
|
1269
|
-
// https://eslint.org/docs/latest/use/configure/configuration-files-deprecated
|
|
1270
|
-
legacy: ["json", "js", "cjs", "yml", "yaml"]
|
|
1271
|
-
};
|
|
1272
|
-
var ESLINT_CONFIG_NAMES = {
|
|
1273
|
-
// https://eslint.org/docs/latest/use/configure/configuration-files#configuration-file-formats
|
|
1274
|
-
flat: ["eslint.config"],
|
|
1275
|
-
// https://eslint.org/docs/latest/use/configure/configuration-files-deprecated
|
|
1276
|
-
legacy: [".eslintrc"]
|
|
1277
|
-
};
|
|
1278
|
-
var CP_ESLINT_CONFIG_NAMES = {
|
|
1279
|
-
flat: [
|
|
1280
|
-
"code-pushup.eslint.config",
|
|
1281
|
-
"eslint.code-pushup.config",
|
|
1282
|
-
"eslint.config.code-pushup",
|
|
1283
|
-
"eslint.strict.config",
|
|
1284
|
-
"eslint.config.strict"
|
|
1285
|
-
],
|
|
1286
|
-
legacy: ["code-pushup.eslintrc", ".eslintrc.code-pushup", ".eslintrc.strict"]
|
|
1287
|
-
};
|
|
1288
|
-
async function findCodePushupEslintConfig(project, format) {
|
|
1289
|
-
return findProjectFile(project, {
|
|
1290
|
-
names: CP_ESLINT_CONFIG_NAMES[format],
|
|
1291
|
-
extensions: ESLINT_CONFIG_EXTENSIONS[format]
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
async function findEslintConfig(project, format) {
|
|
1295
|
-
const options = project.targets?.["lint"]?.options;
|
|
1296
|
-
return options?.eslintConfig ?? await findProjectFile(project, {
|
|
1297
|
-
names: ESLINT_CONFIG_NAMES[format],
|
|
1298
|
-
extensions: ESLINT_CONFIG_EXTENSIONS[format]
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
function getLintFilePatterns(project, format) {
|
|
1302
|
-
const options = project.targets?.["lint"]?.options;
|
|
1303
|
-
const defaultPatterns = format === "legacy" ? `${project.root}/**/*` : project.root;
|
|
1304
|
-
const patterns = options?.lintFilePatterns == null ? [defaultPatterns] : toArray(options.lintFilePatterns);
|
|
1305
|
-
if (format === "legacy") {
|
|
1306
|
-
return [
|
|
1307
|
-
...patterns,
|
|
1308
|
-
// HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
|
|
1309
|
-
// so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
|
|
1310
|
-
// this workaround is only necessary for legacy configs (rules are detected more reliably in flat configs)
|
|
1311
|
-
`${project.root}/*.spec.ts`,
|
|
1312
|
-
// jest/* and vitest/* rules
|
|
1313
|
-
`${project.root}/*.cy.ts`,
|
|
1314
|
-
// cypress/* rules
|
|
1315
|
-
`${project.root}/*.stories.ts`,
|
|
1316
|
-
// storybook/* rules
|
|
1317
|
-
`${project.root}/.storybook/main.ts`
|
|
1318
|
-
// storybook/no-uninstalled-addons rule
|
|
1319
|
-
];
|
|
1320
|
-
}
|
|
1321
|
-
return patterns;
|
|
1322
|
-
}
|
|
1323
|
-
async function findProjectFile(project, file) {
|
|
1324
|
-
for (const name2 of file.names) {
|
|
1325
|
-
for (const ext of file.extensions) {
|
|
1326
|
-
const filename = `./${project.root}/${name2}.${ext}`;
|
|
1327
|
-
if (await fileExists(join5(process.cwd(), filename))) {
|
|
1328
|
-
return filename;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
return void 0;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// packages/plugin-eslint/src/lib/nx/projects-to-config.ts
|
|
1336
|
-
async function nxProjectsToConfig(projectGraph, predicate = () => true) {
|
|
1337
|
-
const { readProjectsConfigurationFromProjectGraph } = await import("@nx/devkit");
|
|
1338
|
-
const projectsConfiguration = readProjectsConfigurationFromProjectGraph(projectGraph);
|
|
1339
|
-
const projects = Object.values(projectsConfiguration.projects).filter((project) => "lint" in (project.targets ?? {})).filter(predicate).sort((a, b) => a.root.localeCompare(b.root));
|
|
1340
|
-
const format = await detectConfigVersion();
|
|
1341
|
-
return Promise.all(
|
|
1342
|
-
projects.map(
|
|
1343
|
-
async (project) => ({
|
|
1344
|
-
eslintrc: await findCodePushupEslintConfig(project, format) ?? await findEslintConfig(project, format),
|
|
1345
|
-
patterns: getLintFilePatterns(project, format)
|
|
1346
|
-
})
|
|
1347
|
-
)
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
// packages/plugin-eslint/src/lib/nx/find-all-projects.ts
|
|
1352
|
-
async function eslintConfigFromAllNxProjects(options = {}) {
|
|
1353
|
-
const { createProjectGraphAsync } = await import("@nx/devkit");
|
|
1354
|
-
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
|
|
1355
|
-
const filteredProjectGraph = filterProjectGraph(
|
|
1356
|
-
projectGraph,
|
|
1357
|
-
options.exclude
|
|
1358
|
-
);
|
|
1359
|
-
return nxProjectsToConfig(filteredProjectGraph);
|
|
1360
|
-
}
|
|
1361
|
-
var eslintConfigFromNxProjects = eslintConfigFromAllNxProjects;
|
|
1362
|
-
|
|
1363
|
-
// packages/plugin-eslint/src/lib/nx/find-project-without-deps.ts
|
|
1364
|
-
async function eslintConfigFromNxProject(projectName) {
|
|
1365
|
-
const { createProjectGraphAsync } = await import("@nx/devkit");
|
|
1366
|
-
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
|
|
1367
|
-
const [project] = await nxProjectsToConfig(
|
|
1368
|
-
projectGraph,
|
|
1369
|
-
({ name: name2 }) => !!name2 && name2 === projectName
|
|
1370
|
-
);
|
|
1371
|
-
if (!project) {
|
|
1372
|
-
throw new Error(`Couldn't find Nx project named "${projectName}"`);
|
|
1373
|
-
}
|
|
1374
|
-
return project;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// packages/plugin-eslint/src/lib/nx/traverse-graph.ts
|
|
1378
|
-
function findAllDependencies(entry, projectGraph) {
|
|
1379
|
-
const results = /* @__PURE__ */ new Set();
|
|
1380
|
-
const queue = [entry];
|
|
1381
|
-
while (queue.length > 0) {
|
|
1382
|
-
const source = queue.shift();
|
|
1383
|
-
const dependencies = projectGraph.dependencies[source];
|
|
1384
|
-
for (const { target } of dependencies ?? []) {
|
|
1385
|
-
if (!results.has(target) && target !== entry) {
|
|
1386
|
-
results.add(target);
|
|
1387
|
-
queue.push(target);
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
return results;
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
// packages/plugin-eslint/src/lib/nx/find-project-with-deps.ts
|
|
1395
|
-
async function eslintConfigFromNxProjectAndDeps(projectName) {
|
|
1396
|
-
const { createProjectGraphAsync } = await import("@nx/devkit");
|
|
1397
|
-
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
|
|
1398
|
-
const dependencies = findAllDependencies(projectName, projectGraph);
|
|
1399
|
-
return nxProjectsToConfig(
|
|
1400
|
-
projectGraph,
|
|
1401
|
-
(project) => !!project.name && (project.name === projectName || dependencies.has(project.name))
|
|
1402
|
-
);
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
// packages/plugin-eslint/src/index.ts
|
|
1406
|
-
var src_default = eslintPlugin;
|
|
1407
|
-
export {
|
|
1408
|
-
src_default as default,
|
|
1409
|
-
eslintConfigFromAllNxProjects,
|
|
1410
|
-
eslintConfigFromNxProject,
|
|
1411
|
-
eslintConfigFromNxProjectAndDeps,
|
|
1412
|
-
eslintConfigFromNxProjects
|
|
1413
|
-
};
|