@code-pushup/eslint-plugin 0.35.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,35 +1,298 @@
1
1
  // packages/plugin-eslint/src/lib/eslint-plugin.ts
2
- import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
3
2
  import { dirname as dirname2, join as join3 } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
5
4
 
6
5
  // packages/plugin-eslint/package.json
7
6
  var name = "@code-pushup/eslint-plugin";
8
- var version = "0.35.0";
7
+ var version = "0.42.0";
9
8
 
10
9
  // packages/plugin-eslint/src/lib/config.ts
11
- import { z } from "zod";
12
- var eslintPluginConfigSchema = z.object({
13
- eslintrc: z.union(
14
- [
15
- z.string({ description: "Path to ESLint config file" }),
16
- z.record(z.string(), z.unknown(), {
17
- description: "ESLint config object"
18
- })
19
- ],
20
- { description: "ESLint config as file path or inline object" }
21
- ),
22
- patterns: z.union([z.string(), z.array(z.string()).min(1)], {
23
- description: "Lint target files. May contain file paths, directory paths or glob patterns"
24
- })
25
- });
10
+ import { z as z16 } from "zod";
26
11
 
27
- // packages/models/src/lib/audit.ts
28
- import { z as z3 } from "zod";
12
+ // packages/utils/src/lib/text-formats/constants.ts
13
+ var NEW_LINE = "\n";
14
+ var TAB = " ";
15
+
16
+ // packages/utils/src/lib/text-formats/html/details.ts
17
+ function details(title, content, cfg = { open: false }) {
18
+ 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.
19
+ NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
20
+ // ⚠️ The blank line ensure Markdown in content is rendered correctly.
21
+ NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
22
+ NEW_LINE}`;
23
+ }
24
+
25
+ // packages/utils/src/lib/text-formats/html/font-style.ts
26
+ var boldElement = "b";
27
+ function bold(text) {
28
+ return `<${boldElement}>${text}</${boldElement}>`;
29
+ }
30
+ var italicElement = "i";
31
+ function italic(text) {
32
+ return `<${italicElement}>${text}</${italicElement}>`;
33
+ }
34
+ var codeElement = "code";
35
+ function code(text) {
36
+ return `<${codeElement}>${text}</${codeElement}>`;
37
+ }
38
+
39
+ // packages/utils/src/lib/text-formats/html/link.ts
40
+ function link(href, text) {
41
+ return `<a href="${href}">${text || href}"</a>`;
42
+ }
43
+
44
+ // packages/utils/src/lib/transform.ts
45
+ function toArray(val) {
46
+ return Array.isArray(val) ? val : [val];
47
+ }
48
+ function objectToKeys(obj) {
49
+ return Object.keys(obj);
50
+ }
51
+ function distinct(array) {
52
+ return [...new Set(array)];
53
+ }
54
+ function capitalize(text) {
55
+ return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
56
+ 1
57
+ )}`;
58
+ }
59
+
60
+ // packages/utils/src/lib/table.ts
61
+ function rowToStringArray({ rows, columns = [] }) {
62
+ if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
63
+ throw new TypeError(
64
+ "Column can`t be object when rows are primitive values"
65
+ );
66
+ }
67
+ return rows.map((row) => {
68
+ if (Array.isArray(row)) {
69
+ return row.map(String);
70
+ }
71
+ const objectRow = row;
72
+ if (columns.length === 0 || typeof columns.at(0) === "string") {
73
+ return Object.values(objectRow).map(String);
74
+ }
75
+ return columns.map(
76
+ ({ key }) => String(objectRow[key])
77
+ );
78
+ });
79
+ }
80
+ function columnsToStringArray({ rows, columns = [] }) {
81
+ const firstRow = rows.at(0);
82
+ const primitiveRows = Array.isArray(firstRow);
83
+ if (typeof columns.at(0) === "string" && !primitiveRows) {
84
+ throw new Error("invalid union type. Caught by model parsing.");
85
+ }
86
+ if (columns.length === 0) {
87
+ if (Array.isArray(firstRow)) {
88
+ return firstRow.map((_, idx) => String(idx));
89
+ }
90
+ return Object.keys(firstRow);
91
+ }
92
+ if (typeof columns.at(0) === "string") {
93
+ return columns.map(String);
94
+ }
95
+ const cols = columns;
96
+ return cols.map(({ label, key }) => label ?? capitalize(key));
97
+ }
98
+ function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
99
+ const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
100
+ if (typeof column === "string") {
101
+ return column;
102
+ } else if (typeof column === "object") {
103
+ return column.align ?? "center";
104
+ } else {
105
+ return "center";
106
+ }
107
+ }
108
+ function getColumnAlignmentForIndex(targetIdx, columns = []) {
109
+ const column = columns.at(targetIdx);
110
+ if (column == null) {
111
+ return "center";
112
+ } else if (typeof column === "string") {
113
+ return column;
114
+ } else if (typeof column === "object") {
115
+ return column.align ?? "center";
116
+ } else {
117
+ return "center";
118
+ }
119
+ }
120
+ function getColumnAlignments({
121
+ rows,
122
+ columns = []
123
+ }) {
124
+ if (rows.at(0) == null) {
125
+ throw new Error("first row can`t be undefined.");
126
+ }
127
+ if (Array.isArray(rows.at(0))) {
128
+ const firstPrimitiveRow = rows.at(0);
129
+ return Array.from({ length: firstPrimitiveRow.length }).map(
130
+ (_, idx) => getColumnAlignmentForIndex(idx, columns)
131
+ );
132
+ }
133
+ const firstObject = rows.at(0);
134
+ return Object.keys(firstObject).map(
135
+ (key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
136
+ );
137
+ }
138
+
139
+ // packages/utils/src/lib/text-formats/html/table.ts
140
+ function wrap(elem, content) {
141
+ return `<${elem}>${content}</${elem}>${NEW_LINE}`;
142
+ }
143
+ function wrapRow(content) {
144
+ const elem = "tr";
145
+ return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
146
+ }
147
+ function table(tableData) {
148
+ if (tableData.rows.length === 0) {
149
+ throw new Error("Data can't be empty");
150
+ }
151
+ const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
152
+ const tableHeaderRow = wrapRow(tableHeaderCols);
153
+ const tableBody = rowToStringArray(tableData).map((arr) => {
154
+ const columns = arr.map((s) => wrap("td", s)).join("");
155
+ return wrapRow(columns);
156
+ }).join("");
157
+ return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
158
+ }
159
+
160
+ // packages/utils/src/lib/text-formats/md/font-style.ts
161
+ var boldWrap = "**";
162
+ function bold2(text) {
163
+ return `${boldWrap}${text}${boldWrap}`;
164
+ }
165
+ var italicWrap = "_";
166
+ function italic2(text) {
167
+ return `${italicWrap}${text}${italicWrap}`;
168
+ }
169
+ var strikeThroughWrap = "~";
170
+ function strikeThrough(text) {
171
+ return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
172
+ }
173
+ var codeWrap = "`";
174
+ function code2(text) {
175
+ return `${codeWrap}${text}${codeWrap}`;
176
+ }
177
+
178
+ // packages/utils/src/lib/text-formats/md/headline.ts
179
+ function headline(text, hierarchy = 1) {
180
+ return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
181
+ }
182
+ function h(text, hierarchy = 1) {
183
+ return headline(text, hierarchy);
184
+ }
185
+ function h1(text) {
186
+ return headline(text, 1);
187
+ }
188
+ function h2(text) {
189
+ return headline(text, 2);
190
+ }
191
+ function h3(text) {
192
+ return headline(text, 3);
193
+ }
194
+ function h4(text) {
195
+ return headline(text, 4);
196
+ }
197
+ function h5(text) {
198
+ return headline(text, 5);
199
+ }
200
+ function h6(text) {
201
+ return headline(text, 6);
202
+ }
203
+
204
+ // packages/utils/src/lib/text-formats/md/image.ts
205
+ function image(src, alt) {
206
+ return `![${alt}](${src})`;
207
+ }
208
+
209
+ // packages/utils/src/lib/text-formats/md/link.ts
210
+ function link2(href, text) {
211
+ return `[${text || href}](${href})`;
212
+ }
213
+
214
+ // packages/utils/src/lib/text-formats/md/list.ts
215
+ function li(text, order = "unordered") {
216
+ const style = order === "unordered" ? "-" : "- [ ]";
217
+ return `${style} ${text}`;
218
+ }
219
+ function indentation(text, level = 1) {
220
+ return `${TAB.repeat(level)}${text}`;
221
+ }
222
+
223
+ // packages/utils/src/lib/text-formats/md/paragraphs.ts
224
+ function paragraphs(...sections) {
225
+ return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
226
+ }
227
+
228
+ // packages/utils/src/lib/text-formats/md/section.ts
229
+ function section(...contents) {
230
+ return `${lines(...contents)}${NEW_LINE}`;
231
+ }
232
+ function lines(...contents) {
233
+ return `${contents.filter(Boolean).join(NEW_LINE)}`;
234
+ }
235
+
236
+ // packages/utils/src/lib/text-formats/md/table.ts
237
+ var alignString = /* @__PURE__ */ new Map([
238
+ ["left", ":--"],
239
+ ["center", ":--:"],
240
+ ["right", "--:"]
241
+ ]);
242
+ function tableRow(rows) {
243
+ return `|${rows.join("|")}|`;
244
+ }
245
+ function table2(data) {
246
+ if (data.rows.length === 0) {
247
+ throw new Error("Data can't be empty");
248
+ }
249
+ const alignmentRow = getColumnAlignments(data).map(
250
+ (s) => alignString.get(s) ?? String(alignString.get("center"))
251
+ );
252
+ return section(
253
+ `${lines(
254
+ tableRow(columnsToStringArray(data)),
255
+ tableRow(alignmentRow),
256
+ ...rowToStringArray(data).map(tableRow)
257
+ )}`
258
+ );
259
+ }
260
+
261
+ // packages/utils/src/lib/text-formats/index.ts
262
+ var md = {
263
+ bold: bold2,
264
+ italic: italic2,
265
+ strikeThrough,
266
+ code: code2,
267
+ link: link2,
268
+ image,
269
+ headline,
270
+ h,
271
+ h1,
272
+ h2,
273
+ h3,
274
+ h4,
275
+ h5,
276
+ h6,
277
+ indentation,
278
+ lines,
279
+ li,
280
+ section,
281
+ paragraphs,
282
+ table: table2
283
+ };
284
+ var html = {
285
+ bold,
286
+ italic,
287
+ code,
288
+ link,
289
+ details,
290
+ table
291
+ };
29
292
 
30
293
  // packages/models/src/lib/implementation/schemas.ts
31
294
  import { MATERIAL_ICONS } from "vscode-material-icons";
32
- import { z as z2 } from "zod";
295
+ import { z } from "zod";
33
296
 
34
297
  // packages/models/src/lib/implementation/limits.ts
35
298
  var MAX_SLUG_LENGTH = 128;
@@ -92,25 +355,26 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
92
355
  }
93
356
 
94
357
  // packages/models/src/lib/implementation/schemas.ts
358
+ var primitiveValueSchema = z.union([z.string(), z.number()]);
95
359
  function executionMetaSchema(options = {
96
360
  descriptionDate: "Execution start date and time",
97
361
  descriptionDuration: "Execution duration in ms"
98
362
  }) {
99
- return z2.object({
100
- date: z2.string({ description: options.descriptionDate }),
101
- duration: z2.number({ description: options.descriptionDuration })
363
+ return z.object({
364
+ date: z.string({ description: options.descriptionDate }),
365
+ duration: z.number({ description: options.descriptionDuration })
102
366
  });
103
367
  }
104
- var slugSchema = z2.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
368
+ var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
105
369
  message: "The slug has to follow the pattern [0-9a-z] followed by multiple optional groups of -[0-9a-z]. e.g. my-slug"
106
370
  }).max(MAX_SLUG_LENGTH, {
107
371
  message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
108
372
  });
109
- var descriptionSchema = z2.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
110
- var urlSchema = z2.string().url();
111
- var docsUrlSchema = urlSchema.optional().or(z2.literal("")).describe("Documentation site");
112
- var titleSchema = z2.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
113
- var scoreSchema = z2.number({
373
+ var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
374
+ var urlSchema = z.string().url();
375
+ var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
376
+ var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
377
+ var scoreSchema = z.number({
114
378
  description: "Value between 0 and 1"
115
379
  }).min(0).max(1);
116
380
  function metaSchema(options) {
@@ -120,7 +384,7 @@ function metaSchema(options) {
120
384
  docsUrlDescription,
121
385
  description
122
386
  } = options ?? {};
123
- return z2.object(
387
+ return z.object(
124
388
  {
125
389
  title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
126
390
  description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
@@ -129,17 +393,17 @@ function metaSchema(options) {
129
393
  { description }
130
394
  );
131
395
  }
132
- var filePathSchema = z2.string().trim().min(1, { message: "path is invalid" });
133
- var fileNameSchema = z2.string().trim().regex(filenameRegex, {
396
+ var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
397
+ var fileNameSchema = z.string().trim().regex(filenameRegex, {
134
398
  message: `The filename has to be valid`
135
399
  }).min(1, { message: "file name is invalid" });
136
- var positiveIntSchema = z2.number().int().positive();
137
- var nonnegativeIntSchema = z2.number().int().nonnegative();
400
+ var positiveIntSchema = z.number().int().positive();
401
+ var nonnegativeIntSchema = z.number().int().nonnegative();
138
402
  function packageVersionSchema(options) {
139
403
  const { versionDescription = "NPM version of the package", required } = options ?? {};
140
- const packageSchema = z2.string({ description: "NPM package name" });
141
- const versionSchema = z2.string({ description: versionDescription });
142
- return z2.object(
404
+ const packageSchema = z.string({ description: "NPM package name" });
405
+ const versionSchema = z.string({ description: versionDescription });
406
+ return z.object(
143
407
  {
144
408
  packageName: required ? packageSchema : packageSchema.optional(),
145
409
  version: required ? versionSchema : versionSchema.optional()
@@ -151,7 +415,7 @@ var weightSchema = nonnegativeIntSchema.describe(
151
415
  "Coefficient for the given score (use weight 0 if only for display)"
152
416
  );
153
417
  function weightedRefSchema(description, slugDescription) {
154
- return z2.object(
418
+ return z.object(
155
419
  {
156
420
  slug: slugSchema.describe(slugDescription),
157
421
  weight: weightSchema.describe("Weight used to calculate score")
@@ -160,10 +424,10 @@ function weightedRefSchema(description, slugDescription) {
160
424
  );
161
425
  }
162
426
  function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
163
- return z2.object(
427
+ return z.object(
164
428
  {
165
429
  slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
166
- refs: z2.array(refSchema).min(1).refine(
430
+ refs: z.array(refSchema).min(1).refine(
167
431
  (refs) => !duplicateCheckFn(refs),
168
432
  (refs) => ({
169
433
  message: duplicateMessageFn(refs)
@@ -175,7 +439,7 @@ function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessa
175
439
  { description }
176
440
  );
177
441
  }
178
- var materialIconSchema = z2.enum(MATERIAL_ICONS, {
442
+ var materialIconSchema = z.enum(MATERIAL_ICONS, {
179
443
  description: "Icon from VSCode Material Icons extension"
180
444
  });
181
445
  function hasNonZeroWeightedRef(refs) {
@@ -183,7 +447,8 @@ function hasNonZeroWeightedRef(refs) {
183
447
  }
184
448
 
185
449
  // packages/models/src/lib/audit.ts
186
- var auditSchema = z3.object({
450
+ import { z as z2 } from "zod";
451
+ var auditSchema = z2.object({
187
452
  slug: slugSchema.describe("ID (unique within plugin)")
188
453
  }).merge(
189
454
  metaSchema({
@@ -193,7 +458,7 @@ var auditSchema = z3.object({
193
458
  description: "List of scorable metrics for the given plugin"
194
459
  })
195
460
  );
196
- var pluginAuditsSchema = z3.array(auditSchema, {
461
+ var pluginAuditsSchema = z2.array(auditSchema, {
197
462
  description: "List of audits maintained in a plugin"
198
463
  }).min(1).refine(
199
464
  (auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
@@ -215,11 +480,11 @@ function getDuplicateSlugsInAudits(audits) {
215
480
  import { z as z5 } from "zod";
216
481
 
217
482
  // packages/models/src/lib/issue.ts
218
- import { z as z4 } from "zod";
219
- var sourceFileLocationSchema = z4.object(
483
+ import { z as z3 } from "zod";
484
+ var sourceFileLocationSchema = z3.object(
220
485
  {
221
486
  file: filePathSchema.describe("Relative path to source file in Git repo"),
222
- position: z4.object(
487
+ position: z3.object(
223
488
  {
224
489
  startLine: positiveIntSchema.describe("Start line"),
225
490
  startColumn: positiveIntSchema.describe("Start column").optional(),
@@ -231,24 +496,69 @@ var sourceFileLocationSchema = z4.object(
231
496
  },
232
497
  { description: "Source file location" }
233
498
  );
234
- var issueSeveritySchema = z4.enum(["info", "warning", "error"], {
499
+ var issueSeveritySchema = z3.enum(["info", "warning", "error"], {
235
500
  description: "Severity level"
236
501
  });
237
- var issueSchema = z4.object(
502
+ var issueSchema = z3.object(
238
503
  {
239
- message: z4.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
504
+ message: z3.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
240
505
  severity: issueSeveritySchema,
241
506
  source: sourceFileLocationSchema.optional()
242
507
  },
243
508
  { description: "Issue information" }
244
509
  );
245
510
 
511
+ // packages/models/src/lib/table.ts
512
+ import { z as z4 } from "zod";
513
+ var tableAlignmentSchema = z4.enum(["left", "center", "right"], {
514
+ description: "Cell alignment"
515
+ });
516
+ var tableColumnObjectSchema = z4.object({
517
+ key: z4.string(),
518
+ label: z4.string().optional(),
519
+ align: tableAlignmentSchema.optional()
520
+ });
521
+ var tableRowObjectSchema = z4.record(primitiveValueSchema, {
522
+ description: "Object row"
523
+ });
524
+ var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
525
+ description: "Primitive row"
526
+ });
527
+ var tableSharedSchema = z4.object({
528
+ title: z4.string().optional().describe("Display title for table")
529
+ });
530
+ var tablePrimitiveSchema = tableSharedSchema.merge(
531
+ z4.object(
532
+ {
533
+ columns: z4.array(tableAlignmentSchema).optional(),
534
+ rows: z4.array(tableRowPrimitiveSchema)
535
+ },
536
+ { description: "Table with primitive rows and optional alignment columns" }
537
+ )
538
+ );
539
+ var tableObjectSchema = tableSharedSchema.merge(
540
+ z4.object(
541
+ {
542
+ columns: z4.union([
543
+ z4.array(tableAlignmentSchema),
544
+ z4.array(tableColumnObjectSchema)
545
+ ]).optional(),
546
+ rows: z4.array(tableRowObjectSchema)
547
+ },
548
+ {
549
+ description: "Table with object rows and optional alignment or object columns"
550
+ }
551
+ )
552
+ );
553
+ var tableSchema = (description = "Table information") => z4.union([tablePrimitiveSchema, tableObjectSchema], { description });
554
+
246
555
  // packages/models/src/lib/audit-output.ts
247
556
  var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
248
557
  var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
249
558
  var auditDetailsSchema = z5.object(
250
559
  {
251
- issues: z5.array(issueSchema, { description: "List of findings" })
560
+ issues: z5.array(issueSchema, { description: "List of findings" }).optional(),
561
+ table: tableSchema("Table of related findings").optional()
252
562
  },
253
563
  { description: "Detailed information" }
254
564
  );
@@ -600,10 +910,14 @@ function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
600
910
  { description }
601
911
  );
602
912
  }
603
- var scorableMetaSchema = z15.object({ slug: slugSchema, title: titleSchema });
913
+ var scorableMetaSchema = z15.object({
914
+ slug: slugSchema,
915
+ title: titleSchema,
916
+ docsUrl: docsUrlSchema
917
+ });
604
918
  var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
605
919
  z15.object({
606
- plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
920
+ plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
607
921
  })
608
922
  );
609
923
  var scorableDiffSchema = scorableMetaSchema.merge(
@@ -740,7 +1054,7 @@ async function ensureDirectoryExists(baseDir) {
740
1054
  await mkdir(baseDir, { recursive: true });
741
1055
  return;
742
1056
  } catch (error) {
743
- ui().logger.error(error.message);
1057
+ ui().logger.info(error.message);
744
1058
  if (error.code !== "EEXIST") {
745
1059
  throw error;
746
1060
  }
@@ -750,27 +1064,65 @@ function pluginWorkDir(slug) {
750
1064
  return join("node_modules", ".code-pushup", slug);
751
1065
  }
752
1066
 
753
- // packages/utils/src/lib/git.ts
1067
+ // packages/utils/src/lib/reports/utils.ts
1068
+ var { image: image2, bold: boldMd } = md;
1069
+
1070
+ // packages/utils/src/lib/git/git.ts
754
1071
  import { simpleGit } from "simple-git";
755
1072
 
756
- // packages/utils/src/lib/transform.ts
757
- function toArray(val) {
758
- return Array.isArray(val) ? val : [val];
759
- }
760
- function objectToKeys(obj) {
761
- return Object.keys(obj);
762
- }
763
- function distinct(array) {
764
- return [...new Set(array)];
765
- }
1073
+ // packages/utils/src/lib/git/git.commits-and-tags.ts
1074
+ import { simpleGit as simpleGit2 } from "simple-git";
1075
+
1076
+ // packages/utils/src/lib/semver.ts
1077
+ import { rcompare, valid } from "semver";
766
1078
 
767
1079
  // packages/utils/src/lib/progress.ts
768
1080
  import chalk3 from "chalk";
769
1081
  import { MultiProgressBars } from "multi-progress-bars";
770
1082
 
1083
+ // packages/utils/src/lib/reports/formatting.ts
1084
+ var { headline: headline2, lines: lines2, link: link3, section: section2, table: table3 } = md;
1085
+
1086
+ // packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
1087
+ var { link: link4, section: section3, h2: h22, lines: lines3, li: li2, bold: boldMd2, h3: h32, indentation: indentation2 } = md;
1088
+
1089
+ // packages/utils/src/lib/reports/generate-md-report.ts
1090
+ var { h1: h12, h2: h23, h3: h33, lines: lines4, link: link5, section: section4, code: codeMd } = md;
1091
+ var { bold: boldHtml, details: details2 } = html;
1092
+
1093
+ // packages/utils/src/lib/reports/generate-md-reports-diff.ts
1094
+ var {
1095
+ h1: h13,
1096
+ h2: h24,
1097
+ lines: lines5,
1098
+ link: link6,
1099
+ bold: boldMd3,
1100
+ italic: italicMd,
1101
+ table: table4,
1102
+ section: section5
1103
+ } = md;
1104
+ var { details: details3 } = html;
1105
+
771
1106
  // packages/utils/src/lib/reports/log-stdout-summary.ts
772
1107
  import chalk4 from "chalk";
773
1108
 
1109
+ // packages/plugin-eslint/src/lib/config.ts
1110
+ var eslintTargetSchema = z16.object({
1111
+ eslintrc: z16.union(
1112
+ [
1113
+ z16.string({ description: "Path to ESLint config file" }),
1114
+ z16.record(z16.string(), z16.unknown(), {
1115
+ description: "ESLint config object"
1116
+ })
1117
+ ],
1118
+ { description: "ESLint config as file path or inline object" }
1119
+ ),
1120
+ patterns: z16.union([z16.string(), z16.array(z16.string()).min(1)], {
1121
+ description: "Lint target files. May contain file paths, directory paths or glob patterns"
1122
+ })
1123
+ });
1124
+ var eslintPluginConfigSchema = z16.union([eslintTargetSchema, z16.array(eslintTargetSchema).min(1)]).transform(toArray);
1125
+
774
1126
  // packages/plugin-eslint/src/lib/meta/hash.ts
775
1127
  import { createHash } from "node:crypto";
776
1128
  function ruleIdToSlug(ruleId, options) {
@@ -784,8 +1136,27 @@ function jsonHash(data, bytes = 8) {
784
1136
  return createHash("shake256", { outputLength: bytes }).update(JSON.stringify(data) || "null").digest("hex");
785
1137
  }
786
1138
 
1139
+ // packages/plugin-eslint/src/lib/setup.ts
1140
+ import { ESLint } from "eslint";
1141
+ function setupESLint(eslintrc) {
1142
+ return new ESLint({
1143
+ ...typeof eslintrc === "string" ? { overrideConfigFile: eslintrc } : { baseConfig: eslintrc },
1144
+ useEslintrc: false,
1145
+ errorOnUnmatchedPattern: false
1146
+ });
1147
+ }
1148
+
787
1149
  // packages/plugin-eslint/src/lib/meta/rules.ts
788
- async function listRules(eslint, patterns) {
1150
+ async function listRules(targets) {
1151
+ const rulesMap = await targets.reduce(async (acc, { eslintrc, patterns }) => {
1152
+ const eslint = setupESLint(eslintrc);
1153
+ const prev = await acc;
1154
+ const curr = await loadRulesMap(eslint, patterns);
1155
+ return mergeRulesMaps(prev, curr);
1156
+ }, Promise.resolve({}));
1157
+ return Object.values(rulesMap).flatMap(Object.values);
1158
+ }
1159
+ async function loadRulesMap(eslint, patterns) {
789
1160
  const configs = await toArray(patterns).reduce(
790
1161
  async (acc, pattern) => [
791
1162
  ...await acc,
@@ -803,31 +1174,39 @@ async function listRules(eslint, patterns) {
803
1174
  suppressedMessages: []
804
1175
  }
805
1176
  ]);
806
- const rulesMap = configs.flatMap((config) => Object.entries(config.rules ?? {})).filter(([, ruleEntry]) => ruleEntry != null && !isRuleOff(ruleEntry)).reduce(
807
- (acc, [ruleId, ruleEntry]) => {
808
- const meta = rulesMeta[ruleId];
809
- if (!meta) {
810
- ui().logger.warning(`Metadata not found for ESLint rule ${ruleId}`);
811
- return acc;
1177
+ return configs.flatMap((config) => Object.entries(config.rules ?? {})).filter(([, ruleEntry]) => ruleEntry != null && !isRuleOff(ruleEntry)).reduce((acc, [ruleId, ruleEntry]) => {
1178
+ const meta = rulesMeta[ruleId];
1179
+ if (!meta) {
1180
+ ui().logger.warning(`Metadata not found for ESLint rule ${ruleId}`);
1181
+ return acc;
1182
+ }
1183
+ const options = toArray(ruleEntry).slice(1);
1184
+ const optionsHash = jsonHash(options);
1185
+ const ruleData = {
1186
+ ruleId,
1187
+ meta,
1188
+ options
1189
+ };
1190
+ return {
1191
+ ...acc,
1192
+ [ruleId]: {
1193
+ ...acc[ruleId],
1194
+ [optionsHash]: ruleData
812
1195
  }
813
- const options = toArray(ruleEntry).slice(1);
814
- const optionsHash = jsonHash(options);
815
- const ruleData = {
816
- ruleId,
817
- meta,
818
- options
819
- };
820
- return {
821
- ...acc,
822
- [ruleId]: {
823
- ...acc[ruleId],
824
- [optionsHash]: ruleData
825
- }
826
- };
827
- },
828
- {}
1196
+ };
1197
+ }, {});
1198
+ }
1199
+ function mergeRulesMaps(prev, curr) {
1200
+ return Object.entries(curr).reduce(
1201
+ (acc, [ruleId, ruleVariants]) => ({
1202
+ ...acc,
1203
+ [ruleId]: {
1204
+ ...acc[ruleId],
1205
+ ...ruleVariants
1206
+ }
1207
+ }),
1208
+ prev
829
1209
  );
830
- return Object.values(rulesMap).flatMap(Object.values);
831
1210
  }
832
1211
  function isRuleOff(entry) {
833
1212
  const level = Array.isArray(entry) ? entry[0] : entry;
@@ -923,7 +1302,7 @@ function ruleToAudit({ ruleId, meta, options }) {
923
1302
  const name2 = ruleId.split("/").at(-1) ?? ruleId;
924
1303
  const plugin = name2 === ruleId ? null : ruleId.slice(0, ruleId.lastIndexOf("/"));
925
1304
  const pluginContext = plugin ? `, from _${plugin}_ plugin` : "";
926
- const lines = [
1305
+ const lines6 = [
927
1306
  `ESLint rule **${name2}**${pluginContext}.`,
928
1307
  ...options?.length ? ["Custom options:"] : [],
929
1308
  ...options?.map(
@@ -933,7 +1312,7 @@ function ruleToAudit({ ruleId, meta, options }) {
933
1312
  return {
934
1313
  slug: ruleIdToSlug(ruleId, options),
935
1314
  title: truncateTitle(meta.docs?.description ?? name2),
936
- description: truncateDescription(lines.join("\n\n")),
1315
+ description: truncateDescription(lines6.join("\n\n")),
937
1316
  ...meta.docs?.url && {
938
1317
  docsUrl: meta.docs.url
939
1318
  }
@@ -941,8 +1320,8 @@ function ruleToAudit({ ruleId, meta, options }) {
941
1320
  }
942
1321
 
943
1322
  // packages/plugin-eslint/src/lib/meta/index.ts
944
- async function listAuditsAndGroups(eslint, patterns) {
945
- const rules = await listRules(eslint, patterns);
1323
+ async function listAuditsAndGroups(targets) {
1324
+ const rules = await listRules(targets);
946
1325
  const audits = rules.map(ruleToAudit);
947
1326
  const groups = [
948
1327
  ...groupsFromRuleTypes(rules),
@@ -954,31 +1333,17 @@ async function listAuditsAndGroups(eslint, patterns) {
954
1333
  // packages/plugin-eslint/src/lib/runner/index.ts
955
1334
  import { writeFile } from "node:fs/promises";
956
1335
  import { dirname, join as join2 } from "node:path";
957
-
958
- // packages/plugin-eslint/src/lib/setup.ts
959
- import { ESLint } from "eslint";
960
- function setupESLint(eslintrc) {
961
- return new ESLint({
962
- ...typeof eslintrc === "string" ? { overrideConfigFile: eslintrc } : { baseConfig: eslintrc },
963
- useEslintrc: false,
964
- errorOnUnmatchedPattern: false
965
- });
966
- }
967
-
968
- // packages/plugin-eslint/src/lib/runner/index.ts
969
1336
  var WORKDIR = pluginWorkDir("eslint");
970
1337
  var RUNNER_OUTPUT_PATH = join2(WORKDIR, "runner-output.json");
971
- var ESLINTRC_PATH = join2(process.cwd(), WORKDIR, ".eslintrc.json");
972
1338
  var PLUGIN_CONFIG_PATH = join2(
973
1339
  process.cwd(),
974
1340
  WORKDIR,
975
1341
  "plugin-config.json"
976
1342
  );
977
- async function createRunnerConfig(scriptPath, audits, eslintrc, patterns) {
1343
+ async function createRunnerConfig(scriptPath, audits, targets) {
978
1344
  const config = {
979
- eslintrc,
980
- slugs: audits.map((audit) => audit.slug),
981
- patterns: toArray(patterns)
1345
+ targets,
1346
+ slugs: audits.map((audit) => audit.slug)
982
1347
  };
983
1348
  await ensureDirectoryExists(dirname(PLUGIN_CONFIG_PATH));
984
1349
  await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config));
@@ -991,14 +1356,8 @@ async function createRunnerConfig(scriptPath, audits, eslintrc, patterns) {
991
1356
 
992
1357
  // packages/plugin-eslint/src/lib/eslint-plugin.ts
993
1358
  async function eslintPlugin(config) {
994
- const { eslintrc, patterns } = eslintPluginConfigSchema.parse(config);
995
- const eslint = setupESLint(eslintrc);
996
- const { audits, groups } = await listAuditsAndGroups(eslint, patterns);
997
- if (typeof eslintrc !== "string") {
998
- await mkdir2(dirname2(ESLINTRC_PATH), { recursive: true });
999
- await writeFile2(ESLINTRC_PATH, JSON.stringify(eslintrc));
1000
- }
1001
- const eslintrcPath = typeof eslintrc === "string" ? eslintrc : ESLINTRC_PATH;
1359
+ const targets = eslintPluginConfigSchema.parse(config);
1360
+ const { audits, groups } = await listAuditsAndGroups(targets);
1002
1361
  const runnerScriptPath = join3(
1003
1362
  fileURLToPath(dirname2(import.meta.url)),
1004
1363
  "bin.js"
@@ -1013,12 +1372,7 @@ async function eslintPlugin(config) {
1013
1372
  version,
1014
1373
  audits,
1015
1374
  groups,
1016
- runner: await createRunnerConfig(
1017
- runnerScriptPath,
1018
- audits,
1019
- eslintrcPath,
1020
- patterns
1021
- )
1375
+ runner: await createRunnerConfig(runnerScriptPath, audits, targets)
1022
1376
  };
1023
1377
  }
1024
1378
 
@@ -1049,33 +1403,27 @@ async function nxProjectsToConfig(projectGraph, predicate = () => true) {
1049
1403
  const { readProjectsConfigurationFromProjectGraph } = await import("@nx/devkit");
1050
1404
  const projectsConfiguration = readProjectsConfigurationFromProjectGraph(projectGraph);
1051
1405
  const projects = Object.values(projectsConfiguration.projects).filter((project) => "lint" in (project.targets ?? {})).filter(predicate).sort((a, b) => a.root.localeCompare(b.root));
1052
- const eslintConfig = {
1053
- root: true,
1054
- overrides: await Promise.all(
1055
- projects.map(async (project) => ({
1056
- files: getLintFilePatterns(project),
1057
- extends: await findCodePushupEslintrc(project) ?? getEslintConfig(project)
1058
- }))
1406
+ return Promise.all(
1407
+ projects.map(
1408
+ async (project) => ({
1409
+ eslintrc: await findCodePushupEslintrc(project) ?? getEslintConfig(project),
1410
+ patterns: [
1411
+ ...getLintFilePatterns(project),
1412
+ // HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
1413
+ // so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
1414
+ // this workaround won't be necessary once flat configs are stable (much easier to find all rules)
1415
+ `${project.sourceRoot}/*.spec.ts`,
1416
+ // jest/* and vitest/* rules
1417
+ `${project.sourceRoot}/*.cy.ts`,
1418
+ // cypress/* rules
1419
+ `${project.sourceRoot}/*.stories.ts`,
1420
+ // storybook/* rules
1421
+ `${project.sourceRoot}/.storybook/main.ts`
1422
+ // storybook/no-uninstalled-addons rule
1423
+ ]
1424
+ })
1059
1425
  )
1060
- };
1061
- const patterns = projects.flatMap((project) => [
1062
- ...getLintFilePatterns(project),
1063
- // HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
1064
- // so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
1065
- // this workaround won't be necessary once flat configs are stable (much easier to find all rules)
1066
- `${project.sourceRoot}/*.spec.ts`,
1067
- // jest/* and vitest/* rules
1068
- `${project.sourceRoot}/*.cy.ts`,
1069
- // cypress/* rules
1070
- `${project.sourceRoot}/*.stories.ts`,
1071
- // storybook/* rules
1072
- `${project.sourceRoot}/.storybook/main.ts`
1073
- // storybook/no-uninstalled-addons rule
1074
- ]);
1075
- return {
1076
- eslintrc: eslintConfig,
1077
- patterns
1078
- };
1426
+ );
1079
1427
  }
1080
1428
 
1081
1429
  // packages/plugin-eslint/src/lib/nx/find-all-projects.ts