@code-pushup/eslint-plugin 0.34.0 → 0.39.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/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // packages/plugin-eslint/src/lib/runner/index.ts
2
- import { writeFile } from "node:fs/promises";
3
- import { dirname, join as join2 } from "node:path";
2
+ import { writeFile as writeFile2 } from "node:fs/promises";
3
+ import { dirname, join as join3 } from "node:path";
4
4
 
5
5
  // packages/models/src/lib/audit.ts
6
6
  import { z as z2 } from "zod";
@@ -578,10 +578,14 @@ function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
578
578
  { description }
579
579
  );
580
580
  }
581
- var scorableMetaSchema = z14.object({ slug: slugSchema, title: titleSchema });
581
+ var scorableMetaSchema = z14.object({
582
+ slug: slugSchema,
583
+ title: titleSchema,
584
+ docsUrl: docsUrlSchema
585
+ });
582
586
  var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
583
587
  z14.object({
584
- plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
588
+ plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
585
589
  })
586
590
  );
587
591
  var scorableDiffSchema = scorableMetaSchema.merge(
@@ -648,6 +652,9 @@ var reportsDiffSchema = z14.object({
648
652
  })
649
653
  );
650
654
 
655
+ // packages/utils/src/lib/execute-process.ts
656
+ import { spawn } from "node:child_process";
657
+
651
658
  // packages/utils/src/lib/file-system.ts
652
659
  import { bundleRequire } from "bundle-require";
653
660
  import chalk2 from "chalk";
@@ -730,7 +737,7 @@ async function ensureDirectoryExists(baseDir) {
730
737
  await mkdir(baseDir, { recursive: true });
731
738
  return;
732
739
  } catch (error) {
733
- ui().logger.error(error.message);
740
+ ui().logger.info(error.message);
734
741
  if (error.code !== "EEXIST") {
735
742
  throw error;
736
743
  }
@@ -741,6 +748,9 @@ function pluginWorkDir(slug) {
741
748
  }
742
749
 
743
750
  // packages/utils/src/lib/reports/utils.ts
751
+ function calcDuration(start, stop) {
752
+ return Math.round((stop ?? performance.now()) - start);
753
+ }
744
754
  function compareIssueSeverity(severity1, severity2) {
745
755
  const levels = {
746
756
  info: 0,
@@ -750,6 +760,51 @@ function compareIssueSeverity(severity1, severity2) {
750
760
  return levels[severity1] - levels[severity2];
751
761
  }
752
762
 
763
+ // packages/utils/src/lib/execute-process.ts
764
+ var ProcessError = class extends Error {
765
+ code;
766
+ stderr;
767
+ stdout;
768
+ constructor(result) {
769
+ super(result.stderr);
770
+ this.code = result.code;
771
+ this.stderr = result.stderr;
772
+ this.stdout = result.stdout;
773
+ }
774
+ };
775
+ function executeProcess(cfg) {
776
+ const { observer, cwd, command, args, ignoreExitCode = false } = cfg;
777
+ const { onStdout, onError, onComplete } = observer ?? {};
778
+ const date = (/* @__PURE__ */ new Date()).toISOString();
779
+ const start = performance.now();
780
+ return new Promise((resolve, reject) => {
781
+ const process2 = spawn(command, args, { cwd, shell: true });
782
+ let stdout = "";
783
+ let stderr = "";
784
+ process2.stdout.on("data", (data) => {
785
+ stdout += String(data);
786
+ onStdout?.(String(data));
787
+ });
788
+ process2.stderr.on("data", (data) => {
789
+ stderr += String(data);
790
+ });
791
+ process2.on("error", (err) => {
792
+ stderr += err.toString();
793
+ });
794
+ process2.on("close", (code) => {
795
+ const timings = { date, duration: calcDuration(start) };
796
+ if (code === 0 || ignoreExitCode) {
797
+ onComplete?.();
798
+ resolve({ code, stdout, stderr, ...timings });
799
+ } else {
800
+ const errorMsg = new ProcessError({ code, stdout, stderr, ...timings });
801
+ onError?.(errorMsg);
802
+ reject(errorMsg);
803
+ }
804
+ });
805
+ });
806
+ }
807
+
753
808
  // packages/utils/src/lib/git.ts
754
809
  import { simpleGit } from "simple-git";
755
810
 
@@ -777,6 +832,11 @@ import { MultiProgressBars } from "multi-progress-bars";
777
832
  // packages/utils/src/lib/reports/log-stdout-summary.ts
778
833
  import chalk4 from "chalk";
779
834
 
835
+ // packages/plugin-eslint/src/lib/runner/lint.ts
836
+ import { rm as rm2, writeFile } from "node:fs/promises";
837
+ import { platform } from "node:os";
838
+ import { join as join2 } from "node:path";
839
+
780
840
  // packages/plugin-eslint/src/lib/setup.ts
781
841
  import { ESLint } from "eslint";
782
842
  function setupESLint(eslintrc) {
@@ -792,34 +852,78 @@ async function lint({
792
852
  eslintrc,
793
853
  patterns
794
854
  }) {
855
+ const results = await executeLint({ eslintrc, patterns });
856
+ const ruleOptionsPerFile = await loadRuleOptionsPerFile(eslintrc, results);
857
+ return { results, ruleOptionsPerFile };
858
+ }
859
+ function executeLint({
860
+ eslintrc,
861
+ patterns
862
+ }) {
863
+ return withConfig(eslintrc, async (configPath) => {
864
+ const { stdout } = await executeProcess({
865
+ command: "npx",
866
+ args: [
867
+ "eslint",
868
+ `--config=${configPath}`,
869
+ "--no-eslintrc",
870
+ "--no-error-on-unmatched-pattern",
871
+ "--format=json",
872
+ ...toArray(patterns).map(
873
+ (pattern) => (
874
+ // globs need to be escaped on Unix
875
+ platform() === "win32" ? pattern : `'${pattern}'`
876
+ )
877
+ )
878
+ ],
879
+ ignoreExitCode: true,
880
+ cwd: process.cwd()
881
+ });
882
+ return JSON.parse(stdout);
883
+ });
884
+ }
885
+ function loadRuleOptionsPerFile(eslintrc, results) {
795
886
  const eslint = setupESLint(eslintrc);
796
- const results = await eslint.lintFiles(patterns);
797
- const ruleOptionsPerFile = await results.reduce(
798
- async (acc, { filePath, messages }) => {
799
- const filesMap = await acc;
800
- const config = await eslint.calculateConfigForFile(
801
- filePath
802
- );
803
- const ruleIds = distinct(
804
- messages.map(({ ruleId }) => ruleId).filter((ruleId) => ruleId != null)
805
- );
806
- const rulesMap = Object.fromEntries(
807
- ruleIds.map((ruleId) => [
808
- ruleId,
809
- toArray(config.rules?.[ruleId] ?? []).slice(1)
810
- ])
811
- );
812
- return {
813
- ...filesMap,
814
- [filePath]: {
815
- ...filesMap[filePath],
816
- ...rulesMap
817
- }
818
- };
819
- },
820
- Promise.resolve({})
887
+ return results.reduce(async (acc, { filePath, messages }) => {
888
+ const filesMap = await acc;
889
+ const config = await eslint.calculateConfigForFile(
890
+ filePath
891
+ );
892
+ const ruleIds = distinct(
893
+ messages.map(({ ruleId }) => ruleId).filter((ruleId) => ruleId != null)
894
+ );
895
+ const rulesMap = Object.fromEntries(
896
+ ruleIds.map((ruleId) => [
897
+ ruleId,
898
+ toArray(config.rules?.[ruleId] ?? []).slice(1)
899
+ ])
900
+ );
901
+ return {
902
+ ...filesMap,
903
+ [filePath]: {
904
+ ...filesMap[filePath],
905
+ ...rulesMap
906
+ }
907
+ };
908
+ }, Promise.resolve({}));
909
+ }
910
+ async function withConfig(eslintrc, fn) {
911
+ if (typeof eslintrc === "string") {
912
+ return fn(eslintrc);
913
+ }
914
+ const configPath = generateTempConfigPath();
915
+ await writeFile(configPath, JSON.stringify(eslintrc));
916
+ try {
917
+ return await fn(configPath);
918
+ } finally {
919
+ await rm2(configPath);
920
+ }
921
+ }
922
+ function generateTempConfigPath() {
923
+ return join2(
924
+ process.cwd(),
925
+ `.eslintrc.${Math.random().toString().slice(2)}.json`
821
926
  );
822
- return { results, ruleOptionsPerFile };
823
927
  }
824
928
 
825
929
  // packages/plugin-eslint/src/lib/meta/hash.ts
@@ -836,6 +940,15 @@ function jsonHash(data, bytes = 8) {
836
940
  }
837
941
 
838
942
  // packages/plugin-eslint/src/lib/runner/transform.ts
943
+ function mergeLinterOutputs(outputs) {
944
+ return outputs.reduce(
945
+ (acc, { results, ruleOptionsPerFile }) => ({
946
+ results: [...acc.results, ...results],
947
+ ruleOptionsPerFile: { ...acc.ruleOptionsPerFile, ...ruleOptionsPerFile }
948
+ }),
949
+ { results: [], ruleOptionsPerFile: {} }
950
+ );
951
+ }
839
952
  function lintResultsToAudits({
840
953
  results,
841
954
  ruleOptionsPerFile
@@ -845,7 +958,9 @@ function lintResultsToAudits({
845
958
  ).reduce((acc, issue) => {
846
959
  const { ruleId, message, filePath } = issue;
847
960
  if (!ruleId) {
848
- ui().logger.warning(`ESLint core error - ${message}`);
961
+ ui().logger.warning(
962
+ `ESLint core error - ${message} (file: ${filePath})`
963
+ );
849
964
  return acc;
850
965
  }
851
966
  const options = ruleOptionsPerFile[filePath]?.[ruleId] ?? [];
@@ -903,20 +1018,21 @@ function convertSeverity(severity) {
903
1018
 
904
1019
  // packages/plugin-eslint/src/lib/runner/index.ts
905
1020
  var WORKDIR = pluginWorkDir("eslint");
906
- var RUNNER_OUTPUT_PATH = join2(WORKDIR, "runner-output.json");
907
- var ESLINTRC_PATH = join2(process.cwd(), WORKDIR, ".eslintrc.json");
908
- var PLUGIN_CONFIG_PATH = join2(
1021
+ var RUNNER_OUTPUT_PATH = join3(WORKDIR, "runner-output.json");
1022
+ var PLUGIN_CONFIG_PATH = join3(
909
1023
  process.cwd(),
910
1024
  WORKDIR,
911
1025
  "plugin-config.json"
912
1026
  );
913
1027
  async function executeRunner() {
914
- const { slugs, eslintrc, patterns } = await readJsonFile(PLUGIN_CONFIG_PATH);
915
- const lintResults = await lint({
916
- // if file created from inline object, provide inline to preserve relative links
917
- eslintrc: eslintrc === ESLINTRC_PATH ? await readJsonFile(eslintrc) : eslintrc,
918
- patterns
919
- });
1028
+ const { slugs, targets } = await readJsonFile(
1029
+ PLUGIN_CONFIG_PATH
1030
+ );
1031
+ const linterOutputs = await targets.reduce(
1032
+ async (acc, target) => [...await acc, await lint(target)],
1033
+ Promise.resolve([])
1034
+ );
1035
+ const lintResults = mergeLinterOutputs(linterOutputs);
920
1036
  const failedAudits = lintResultsToAudits(lintResults);
921
1037
  const audits = slugs.map(
922
1038
  (slug) => failedAudits.find((audit) => audit.slug === slug) ?? {
@@ -928,7 +1044,7 @@ async function executeRunner() {
928
1044
  }
929
1045
  );
930
1046
  await ensureDirectoryExists(dirname(RUNNER_OUTPUT_PATH));
931
- await writeFile(RUNNER_OUTPUT_PATH, JSON.stringify(audits));
1047
+ await writeFile2(RUNNER_OUTPUT_PATH, JSON.stringify(audits));
932
1048
  }
933
1049
 
934
1050
  // packages/plugin-eslint/src/bin.ts
package/index.js CHANGED
@@ -1,35 +1,20 @@
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.34.0";
7
+ var version = "0.39.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 z15 } from "zod";
26
11
 
27
12
  // packages/models/src/lib/audit.ts
28
- import { z as z3 } from "zod";
13
+ import { z as z2 } from "zod";
29
14
 
30
15
  // packages/models/src/lib/implementation/schemas.ts
31
16
  import { MATERIAL_ICONS } from "vscode-material-icons";
32
- import { z as z2 } from "zod";
17
+ import { z } from "zod";
33
18
 
34
19
  // packages/models/src/lib/implementation/limits.ts
35
20
  var MAX_SLUG_LENGTH = 128;
@@ -96,21 +81,21 @@ function executionMetaSchema(options = {
96
81
  descriptionDate: "Execution start date and time",
97
82
  descriptionDuration: "Execution duration in ms"
98
83
  }) {
99
- return z2.object({
100
- date: z2.string({ description: options.descriptionDate }),
101
- duration: z2.number({ description: options.descriptionDuration })
84
+ return z.object({
85
+ date: z.string({ description: options.descriptionDate }),
86
+ duration: z.number({ description: options.descriptionDuration })
102
87
  });
103
88
  }
104
- var slugSchema = z2.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
89
+ var slugSchema = z.string({ description: "Unique ID (human-readable, URL-safe)" }).regex(slugRegex, {
105
90
  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
91
  }).max(MAX_SLUG_LENGTH, {
107
92
  message: `slug can be max ${MAX_SLUG_LENGTH} characters long`
108
93
  });
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({
94
+ var descriptionSchema = z.string({ description: "Description (markdown)" }).max(MAX_DESCRIPTION_LENGTH).optional();
95
+ var urlSchema = z.string().url();
96
+ var docsUrlSchema = urlSchema.optional().or(z.literal("")).describe("Documentation site");
97
+ var titleSchema = z.string({ description: "Descriptive name" }).max(MAX_TITLE_LENGTH);
98
+ var scoreSchema = z.number({
114
99
  description: "Value between 0 and 1"
115
100
  }).min(0).max(1);
116
101
  function metaSchema(options) {
@@ -120,7 +105,7 @@ function metaSchema(options) {
120
105
  docsUrlDescription,
121
106
  description
122
107
  } = options ?? {};
123
- return z2.object(
108
+ return z.object(
124
109
  {
125
110
  title: titleDescription ? titleSchema.describe(titleDescription) : titleSchema,
126
111
  description: descriptionDescription ? descriptionSchema.describe(descriptionDescription) : descriptionSchema,
@@ -129,17 +114,17 @@ function metaSchema(options) {
129
114
  { description }
130
115
  );
131
116
  }
132
- var filePathSchema = z2.string().trim().min(1, { message: "path is invalid" });
133
- var fileNameSchema = z2.string().trim().regex(filenameRegex, {
117
+ var filePathSchema = z.string().trim().min(1, { message: "path is invalid" });
118
+ var fileNameSchema = z.string().trim().regex(filenameRegex, {
134
119
  message: `The filename has to be valid`
135
120
  }).min(1, { message: "file name is invalid" });
136
- var positiveIntSchema = z2.number().int().positive();
137
- var nonnegativeIntSchema = z2.number().int().nonnegative();
121
+ var positiveIntSchema = z.number().int().positive();
122
+ var nonnegativeIntSchema = z.number().int().nonnegative();
138
123
  function packageVersionSchema(options) {
139
124
  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(
125
+ const packageSchema = z.string({ description: "NPM package name" });
126
+ const versionSchema = z.string({ description: versionDescription });
127
+ return z.object(
143
128
  {
144
129
  packageName: required ? packageSchema : packageSchema.optional(),
145
130
  version: required ? versionSchema : versionSchema.optional()
@@ -151,7 +136,7 @@ var weightSchema = nonnegativeIntSchema.describe(
151
136
  "Coefficient for the given score (use weight 0 if only for display)"
152
137
  );
153
138
  function weightedRefSchema(description, slugDescription) {
154
- return z2.object(
139
+ return z.object(
155
140
  {
156
141
  slug: slugSchema.describe(slugDescription),
157
142
  weight: weightSchema.describe("Weight used to calculate score")
@@ -160,10 +145,10 @@ function weightedRefSchema(description, slugDescription) {
160
145
  );
161
146
  }
162
147
  function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessageFn) {
163
- return z2.object(
148
+ return z.object(
164
149
  {
165
150
  slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
166
- refs: z2.array(refSchema).min(1).refine(
151
+ refs: z.array(refSchema).min(1).refine(
167
152
  (refs) => !duplicateCheckFn(refs),
168
153
  (refs) => ({
169
154
  message: duplicateMessageFn(refs)
@@ -175,7 +160,7 @@ function scorableSchema(description, refSchema, duplicateCheckFn, duplicateMessa
175
160
  { description }
176
161
  );
177
162
  }
178
- var materialIconSchema = z2.enum(MATERIAL_ICONS, {
163
+ var materialIconSchema = z.enum(MATERIAL_ICONS, {
179
164
  description: "Icon from VSCode Material Icons extension"
180
165
  });
181
166
  function hasNonZeroWeightedRef(refs) {
@@ -183,7 +168,7 @@ function hasNonZeroWeightedRef(refs) {
183
168
  }
184
169
 
185
170
  // packages/models/src/lib/audit.ts
186
- var auditSchema = z3.object({
171
+ var auditSchema = z2.object({
187
172
  slug: slugSchema.describe("ID (unique within plugin)")
188
173
  }).merge(
189
174
  metaSchema({
@@ -193,7 +178,7 @@ var auditSchema = z3.object({
193
178
  description: "List of scorable metrics for the given plugin"
194
179
  })
195
180
  );
196
- var pluginAuditsSchema = z3.array(auditSchema, {
181
+ var pluginAuditsSchema = z2.array(auditSchema, {
197
182
  description: "List of audits maintained in a plugin"
198
183
  }).min(1).refine(
199
184
  (auditMetadata) => !getDuplicateSlugsInAudits(auditMetadata),
@@ -212,14 +197,14 @@ function getDuplicateSlugsInAudits(audits) {
212
197
  }
213
198
 
214
199
  // packages/models/src/lib/audit-output.ts
215
- import { z as z5 } from "zod";
200
+ import { z as z4 } from "zod";
216
201
 
217
202
  // packages/models/src/lib/issue.ts
218
- import { z as z4 } from "zod";
219
- var sourceFileLocationSchema = z4.object(
203
+ import { z as z3 } from "zod";
204
+ var sourceFileLocationSchema = z3.object(
220
205
  {
221
206
  file: filePathSchema.describe("Relative path to source file in Git repo"),
222
- position: z4.object(
207
+ position: z3.object(
223
208
  {
224
209
  startLine: positiveIntSchema.describe("Start line"),
225
210
  startColumn: positiveIntSchema.describe("Start column").optional(),
@@ -231,12 +216,12 @@ var sourceFileLocationSchema = z4.object(
231
216
  },
232
217
  { description: "Source file location" }
233
218
  );
234
- var issueSeveritySchema = z4.enum(["info", "warning", "error"], {
219
+ var issueSeveritySchema = z3.enum(["info", "warning", "error"], {
235
220
  description: "Severity level"
236
221
  });
237
- var issueSchema = z4.object(
222
+ var issueSchema = z3.object(
238
223
  {
239
- message: z4.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
224
+ message: z3.string({ description: "Descriptive error message" }).max(MAX_ISSUE_MESSAGE_LENGTH),
240
225
  severity: issueSeveritySchema,
241
226
  source: sourceFileLocationSchema.optional()
242
227
  },
@@ -245,14 +230,14 @@ var issueSchema = z4.object(
245
230
 
246
231
  // packages/models/src/lib/audit-output.ts
247
232
  var auditValueSchema = nonnegativeIntSchema.describe("Raw numeric value");
248
- var auditDisplayValueSchema = z5.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
249
- var auditDetailsSchema = z5.object(
233
+ var auditDisplayValueSchema = z4.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" }).optional();
234
+ var auditDetailsSchema = z4.object(
250
235
  {
251
- issues: z5.array(issueSchema, { description: "List of findings" })
236
+ issues: z4.array(issueSchema, { description: "List of findings" })
252
237
  },
253
238
  { description: "Detailed information" }
254
239
  );
255
- var auditOutputSchema = z5.object(
240
+ var auditOutputSchema = z4.object(
256
241
  {
257
242
  slug: slugSchema.describe("Reference to audit"),
258
243
  displayValue: auditDisplayValueSchema,
@@ -262,7 +247,7 @@ var auditOutputSchema = z5.object(
262
247
  },
263
248
  { description: "Audit information" }
264
249
  );
265
- var auditOutputsSchema = z5.array(auditOutputSchema, {
250
+ var auditOutputsSchema = z4.array(auditOutputSchema, {
266
251
  description: "List of JSON formatted audit output emitted by the runner process of a plugin"
267
252
  }).refine(
268
253
  (audits) => !getDuplicateSlugsInAudits2(audits),
@@ -279,13 +264,13 @@ function getDuplicateSlugsInAudits2(audits) {
279
264
  }
280
265
 
281
266
  // packages/models/src/lib/category-config.ts
282
- import { z as z6 } from "zod";
267
+ import { z as z5 } from "zod";
283
268
  var categoryRefSchema = weightedRefSchema(
284
269
  "Weighted references to audits and/or groups for the category",
285
270
  "Slug of an audit or group (depending on `type`)"
286
271
  ).merge(
287
- z6.object({
288
- type: z6.enum(["audit", "group"], {
272
+ z5.object({
273
+ type: z5.enum(["audit", "group"], {
289
274
  description: "Discriminant for reference kind, affects where `slug` is looked up"
290
275
  }),
291
276
  plugin: slugSchema.describe(
@@ -306,8 +291,8 @@ var categoryConfigSchema = scorableSchema(
306
291
  description: "Meta info for category"
307
292
  })
308
293
  ).merge(
309
- z6.object({
310
- isBinary: z6.boolean({
294
+ z5.object({
295
+ isBinary: z5.boolean({
311
296
  description: 'Is this a binary category (i.e. only a perfect score considered a "pass")?'
312
297
  }).optional()
313
298
  })
@@ -323,7 +308,7 @@ function getDuplicateRefsInCategoryMetrics(metrics) {
323
308
  metrics.map(({ slug, type, plugin }) => `${type} :: ${plugin} / ${slug}`)
324
309
  );
325
310
  }
326
- var categoriesSchema = z6.array(categoryConfigSchema, {
311
+ var categoriesSchema = z5.array(categoryConfigSchema, {
327
312
  description: "Categorization of individual audits"
328
313
  }).refine(
329
314
  (categoryCfg) => !getDuplicateSlugCategories(categoryCfg),
@@ -342,18 +327,18 @@ function getDuplicateSlugCategories(categories) {
342
327
  }
343
328
 
344
329
  // packages/models/src/lib/commit.ts
345
- import { z as z7 } from "zod";
346
- var commitSchema = z7.object(
330
+ import { z as z6 } from "zod";
331
+ var commitSchema = z6.object(
347
332
  {
348
- hash: z7.string({ description: "Commit SHA (full)" }).regex(
333
+ hash: z6.string({ description: "Commit SHA (full)" }).regex(
349
334
  /^[\da-f]{40}$/,
350
335
  "Commit SHA should be a 40-character hexadecimal string"
351
336
  ),
352
- message: z7.string({ description: "Commit message" }),
353
- date: z7.coerce.date({
337
+ message: z6.string({ description: "Commit message" }),
338
+ date: z6.coerce.date({
354
339
  description: "Date and time when commit was authored"
355
340
  }),
356
- author: z7.string({
341
+ author: z6.string({
357
342
  description: "Commit author name"
358
343
  }).trim()
359
344
  },
@@ -361,22 +346,22 @@ var commitSchema = z7.object(
361
346
  );
362
347
 
363
348
  // packages/models/src/lib/core-config.ts
364
- import { z as z13 } from "zod";
349
+ import { z as z12 } from "zod";
365
350
 
366
351
  // packages/models/src/lib/persist-config.ts
367
- import { z as z8 } from "zod";
368
- var formatSchema = z8.enum(["json", "md"]);
369
- var persistConfigSchema = z8.object({
352
+ import { z as z7 } from "zod";
353
+ var formatSchema = z7.enum(["json", "md"]);
354
+ var persistConfigSchema = z7.object({
370
355
  outputDir: filePathSchema.describe("Artifacts folder").optional(),
371
356
  filename: fileNameSchema.describe("Artifacts file name (without extension)").optional(),
372
- format: z8.array(formatSchema).optional()
357
+ format: z7.array(formatSchema).optional()
373
358
  });
374
359
 
375
360
  // packages/models/src/lib/plugin-config.ts
376
- import { z as z11 } from "zod";
361
+ import { z as z10 } from "zod";
377
362
 
378
363
  // packages/models/src/lib/group.ts
379
- import { z as z9 } from "zod";
364
+ import { z as z8 } from "zod";
380
365
  var groupRefSchema = weightedRefSchema(
381
366
  "Weighted reference to a group",
382
367
  "Reference slug to a group within this plugin (e.g. 'max-lines')"
@@ -393,7 +378,7 @@ var groupSchema = scorableSchema(
393
378
  getDuplicateRefsInGroups,
394
379
  duplicateRefsInGroupsErrorMsg
395
380
  ).merge(groupMetaSchema);
396
- var groupsSchema = z9.array(groupSchema, {
381
+ var groupsSchema = z8.array(groupSchema, {
397
382
  description: "List of groups"
398
383
  }).optional().refine(
399
384
  (groups) => !getDuplicateSlugsInGroups(groups),
@@ -421,14 +406,14 @@ function getDuplicateSlugsInGroups(groups) {
421
406
  }
422
407
 
423
408
  // packages/models/src/lib/runner-config.ts
424
- import { z as z10 } from "zod";
425
- var outputTransformSchema = z10.function().args(z10.unknown()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
426
- var runnerConfigSchema = z10.object(
409
+ import { z as z9 } from "zod";
410
+ var outputTransformSchema = z9.function().args(z9.unknown()).returns(z9.union([auditOutputsSchema, z9.promise(auditOutputsSchema)]));
411
+ var runnerConfigSchema = z9.object(
427
412
  {
428
- command: z10.string({
413
+ command: z9.string({
429
414
  description: "Shell command to execute"
430
415
  }),
431
- args: z10.array(z10.string({ description: "Command arguments" })).optional(),
416
+ args: z9.array(z9.string({ description: "Command arguments" })).optional(),
432
417
  outputFile: filePathSchema.describe("Output path"),
433
418
  outputTransform: outputTransformSchema.optional()
434
419
  },
@@ -436,8 +421,8 @@ var runnerConfigSchema = z10.object(
436
421
  description: "How to execute runner"
437
422
  }
438
423
  );
439
- var onProgressSchema = z10.function().args(z10.unknown()).returns(z10.void());
440
- var runnerFunctionSchema = z10.function().args(onProgressSchema.optional()).returns(z10.union([auditOutputsSchema, z10.promise(auditOutputsSchema)]));
424
+ var onProgressSchema = z9.function().args(z9.unknown()).returns(z9.void());
425
+ var runnerFunctionSchema = z9.function().args(onProgressSchema.optional()).returns(z9.union([auditOutputsSchema, z9.promise(auditOutputsSchema)]));
441
426
 
442
427
  // packages/models/src/lib/plugin-config.ts
443
428
  var pluginMetaSchema = packageVersionSchema().merge(
@@ -448,13 +433,13 @@ var pluginMetaSchema = packageVersionSchema().merge(
448
433
  description: "Plugin metadata"
449
434
  })
450
435
  ).merge(
451
- z11.object({
436
+ z10.object({
452
437
  slug: slugSchema.describe("Unique plugin slug within core config"),
453
438
  icon: materialIconSchema
454
439
  })
455
440
  );
456
- var pluginDataSchema = z11.object({
457
- runner: z11.union([runnerConfigSchema, runnerFunctionSchema]),
441
+ var pluginDataSchema = z10.object({
442
+ runner: z10.union([runnerConfigSchema, runnerFunctionSchema]),
458
443
  audits: pluginAuditsSchema,
459
444
  groups: groupsSchema
460
445
  });
@@ -480,22 +465,22 @@ function getMissingRefsFromGroups(pluginCfg) {
480
465
  }
481
466
 
482
467
  // packages/models/src/lib/upload-config.ts
483
- import { z as z12 } from "zod";
484
- var uploadConfigSchema = z12.object({
468
+ import { z as z11 } from "zod";
469
+ var uploadConfigSchema = z11.object({
485
470
  server: urlSchema.describe("URL of deployed portal API"),
486
- apiKey: z12.string({
471
+ apiKey: z11.string({
487
472
  description: "API key with write access to portal (use `process.env` for security)"
488
473
  }),
489
474
  organization: slugSchema.describe(
490
475
  "Organization slug from Code PushUp portal"
491
476
  ),
492
477
  project: slugSchema.describe("Project slug from Code PushUp portal"),
493
- timeout: z12.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
478
+ timeout: z11.number({ description: "Request timeout in minutes (default is 5)" }).positive().int().optional()
494
479
  });
495
480
 
496
481
  // packages/models/src/lib/core-config.ts
497
- var unrefinedCoreConfigSchema = z13.object({
498
- plugins: z13.array(pluginConfigSchema, {
482
+ var unrefinedCoreConfigSchema = z12.object({
483
+ plugins: z12.array(pluginConfigSchema, {
499
484
  description: "List of plugins to be used (official, community-provided, or custom)"
500
485
  }).min(1),
501
486
  /** portal configuration for persisting results */
@@ -518,7 +503,7 @@ function refineCoreConfig(schema) {
518
503
  }
519
504
 
520
505
  // packages/models/src/lib/report.ts
521
- import { z as z14 } from "zod";
506
+ import { z as z13 } from "zod";
522
507
  var auditReportSchema = auditSchema.merge(auditOutputSchema);
523
508
  var pluginReportSchema = pluginMetaSchema.merge(
524
509
  executionMetaSchema({
@@ -526,9 +511,9 @@ var pluginReportSchema = pluginMetaSchema.merge(
526
511
  descriptionDuration: "Duration of the plugin run in ms"
527
512
  })
528
513
  ).merge(
529
- z14.object({
530
- audits: z14.array(auditReportSchema).min(1),
531
- groups: z14.array(groupSchema).optional()
514
+ z13.object({
515
+ audits: z13.array(auditReportSchema).min(1),
516
+ groups: z13.array(groupSchema).optional()
532
517
  })
533
518
  ).refine(
534
519
  (pluginReport) => !getMissingRefsFromGroups2(pluginReport.audits, pluginReport.groups ?? []),
@@ -562,10 +547,10 @@ var reportSchema = packageVersionSchema({
562
547
  descriptionDuration: "Duration of the collect run in ms"
563
548
  })
564
549
  ).merge(
565
- z14.object(
550
+ z13.object(
566
551
  {
567
- categories: z14.array(categoryConfigSchema),
568
- plugins: z14.array(pluginReportSchema).min(1),
552
+ categories: z13.array(categoryConfigSchema),
553
+ plugins: z13.array(pluginReportSchema).min(1),
569
554
  commit: commitSchema.describe("Git commit for which report was collected").nullable()
570
555
  },
571
556
  { description: "Collect output data" }
@@ -581,36 +566,40 @@ var reportSchema = packageVersionSchema({
581
566
  );
582
567
 
583
568
  // packages/models/src/lib/reports-diff.ts
584
- import { z as z15 } from "zod";
569
+ import { z as z14 } from "zod";
585
570
  function makeComparisonSchema(schema) {
586
571
  const sharedDescription = schema.description || "Result";
587
- return z15.object({
572
+ return z14.object({
588
573
  before: schema.describe(`${sharedDescription} (source commit)`),
589
574
  after: schema.describe(`${sharedDescription} (target commit)`)
590
575
  });
591
576
  }
592
577
  function makeArraysComparisonSchema(diffSchema, resultSchema, description) {
593
- return z15.object(
578
+ return z14.object(
594
579
  {
595
- changed: z15.array(diffSchema),
596
- unchanged: z15.array(resultSchema),
597
- added: z15.array(resultSchema),
598
- removed: z15.array(resultSchema)
580
+ changed: z14.array(diffSchema),
581
+ unchanged: z14.array(resultSchema),
582
+ added: z14.array(resultSchema),
583
+ removed: z14.array(resultSchema)
599
584
  },
600
585
  { description }
601
586
  );
602
587
  }
603
- var scorableMetaSchema = z15.object({ slug: slugSchema, title: titleSchema });
588
+ var scorableMetaSchema = z14.object({
589
+ slug: slugSchema,
590
+ title: titleSchema,
591
+ docsUrl: docsUrlSchema
592
+ });
604
593
  var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
605
- z15.object({
606
- plugin: pluginMetaSchema.pick({ slug: true, title: true }).describe("Plugin which defines it")
594
+ z14.object({
595
+ plugin: pluginMetaSchema.pick({ slug: true, title: true, docsUrl: true }).describe("Plugin which defines it")
607
596
  })
608
597
  );
609
598
  var scorableDiffSchema = scorableMetaSchema.merge(
610
- z15.object({
599
+ z14.object({
611
600
  scores: makeComparisonSchema(scoreSchema).merge(
612
- z15.object({
613
- diff: z15.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
601
+ z14.object({
602
+ diff: z14.number().min(-1).max(1).describe("Score change (`scores.after - scores.before`)")
614
603
  })
615
604
  ).describe("Score comparison")
616
605
  })
@@ -621,10 +610,10 @@ var scorableWithPluginDiffSchema = scorableDiffSchema.merge(
621
610
  var categoryDiffSchema = scorableDiffSchema;
622
611
  var groupDiffSchema = scorableWithPluginDiffSchema;
623
612
  var auditDiffSchema = scorableWithPluginDiffSchema.merge(
624
- z15.object({
613
+ z14.object({
625
614
  values: makeComparisonSchema(auditValueSchema).merge(
626
- z15.object({
627
- diff: z15.number().int().describe("Value change (`values.after - values.before`)")
615
+ z14.object({
616
+ diff: z14.number().int().describe("Value change (`values.after - values.before`)")
628
617
  })
629
618
  ).describe("Audit `value` comparison"),
630
619
  displayValues: makeComparisonSchema(auditDisplayValueSchema).describe(
@@ -633,15 +622,15 @@ var auditDiffSchema = scorableWithPluginDiffSchema.merge(
633
622
  })
634
623
  );
635
624
  var categoryResultSchema = scorableMetaSchema.merge(
636
- z15.object({ score: scoreSchema })
625
+ z14.object({ score: scoreSchema })
637
626
  );
638
627
  var groupResultSchema = scorableWithPluginMetaSchema.merge(
639
- z15.object({ score: scoreSchema })
628
+ z14.object({ score: scoreSchema })
640
629
  );
641
630
  var auditResultSchema = scorableWithPluginMetaSchema.merge(
642
631
  auditOutputSchema.pick({ score: true, value: true, displayValue: true })
643
632
  );
644
- var reportsDiffSchema = z15.object({
633
+ var reportsDiffSchema = z14.object({
645
634
  commits: makeComparisonSchema(commitSchema).nullable().describe("Commits identifying compared reports"),
646
635
  categories: makeArraysComparisonSchema(
647
636
  categoryDiffSchema,
@@ -740,7 +729,7 @@ async function ensureDirectoryExists(baseDir) {
740
729
  await mkdir(baseDir, { recursive: true });
741
730
  return;
742
731
  } catch (error) {
743
- ui().logger.error(error.message);
732
+ ui().logger.info(error.message);
744
733
  if (error.code !== "EEXIST") {
745
734
  throw error;
746
735
  }
@@ -771,6 +760,23 @@ import { MultiProgressBars } from "multi-progress-bars";
771
760
  // packages/utils/src/lib/reports/log-stdout-summary.ts
772
761
  import chalk4 from "chalk";
773
762
 
763
+ // packages/plugin-eslint/src/lib/config.ts
764
+ var eslintTargetSchema = z15.object({
765
+ eslintrc: z15.union(
766
+ [
767
+ z15.string({ description: "Path to ESLint config file" }),
768
+ z15.record(z15.string(), z15.unknown(), {
769
+ description: "ESLint config object"
770
+ })
771
+ ],
772
+ { description: "ESLint config as file path or inline object" }
773
+ ),
774
+ patterns: z15.union([z15.string(), z15.array(z15.string()).min(1)], {
775
+ description: "Lint target files. May contain file paths, directory paths or glob patterns"
776
+ })
777
+ });
778
+ var eslintPluginConfigSchema = z15.union([eslintTargetSchema, z15.array(eslintTargetSchema).min(1)]).transform(toArray);
779
+
774
780
  // packages/plugin-eslint/src/lib/meta/hash.ts
775
781
  import { createHash } from "node:crypto";
776
782
  function ruleIdToSlug(ruleId, options) {
@@ -784,8 +790,27 @@ function jsonHash(data, bytes = 8) {
784
790
  return createHash("shake256", { outputLength: bytes }).update(JSON.stringify(data) || "null").digest("hex");
785
791
  }
786
792
 
793
+ // packages/plugin-eslint/src/lib/setup.ts
794
+ import { ESLint } from "eslint";
795
+ function setupESLint(eslintrc) {
796
+ return new ESLint({
797
+ ...typeof eslintrc === "string" ? { overrideConfigFile: eslintrc } : { baseConfig: eslintrc },
798
+ useEslintrc: false,
799
+ errorOnUnmatchedPattern: false
800
+ });
801
+ }
802
+
787
803
  // packages/plugin-eslint/src/lib/meta/rules.ts
788
- async function listRules(eslint, patterns) {
804
+ async function listRules(targets) {
805
+ const rulesMap = await targets.reduce(async (acc, { eslintrc, patterns }) => {
806
+ const eslint = setupESLint(eslintrc);
807
+ const prev = await acc;
808
+ const curr = await loadRulesMap(eslint, patterns);
809
+ return mergeRulesMaps(prev, curr);
810
+ }, Promise.resolve({}));
811
+ return Object.values(rulesMap).flatMap(Object.values);
812
+ }
813
+ async function loadRulesMap(eslint, patterns) {
789
814
  const configs = await toArray(patterns).reduce(
790
815
  async (acc, pattern) => [
791
816
  ...await acc,
@@ -803,31 +828,39 @@ async function listRules(eslint, patterns) {
803
828
  suppressedMessages: []
804
829
  }
805
830
  ]);
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;
831
+ return configs.flatMap((config) => Object.entries(config.rules ?? {})).filter(([, ruleEntry]) => ruleEntry != null && !isRuleOff(ruleEntry)).reduce((acc, [ruleId, ruleEntry]) => {
832
+ const meta = rulesMeta[ruleId];
833
+ if (!meta) {
834
+ ui().logger.warning(`Metadata not found for ESLint rule ${ruleId}`);
835
+ return acc;
836
+ }
837
+ const options = toArray(ruleEntry).slice(1);
838
+ const optionsHash = jsonHash(options);
839
+ const ruleData = {
840
+ ruleId,
841
+ meta,
842
+ options
843
+ };
844
+ return {
845
+ ...acc,
846
+ [ruleId]: {
847
+ ...acc[ruleId],
848
+ [optionsHash]: ruleData
812
849
  }
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
- {}
850
+ };
851
+ }, {});
852
+ }
853
+ function mergeRulesMaps(prev, curr) {
854
+ return Object.entries(curr).reduce(
855
+ (acc, [ruleId, ruleVariants]) => ({
856
+ ...acc,
857
+ [ruleId]: {
858
+ ...acc[ruleId],
859
+ ...ruleVariants
860
+ }
861
+ }),
862
+ prev
829
863
  );
830
- return Object.values(rulesMap).flatMap(Object.values);
831
864
  }
832
865
  function isRuleOff(entry) {
833
866
  const level = Array.isArray(entry) ? entry[0] : entry;
@@ -941,8 +974,8 @@ function ruleToAudit({ ruleId, meta, options }) {
941
974
  }
942
975
 
943
976
  // packages/plugin-eslint/src/lib/meta/index.ts
944
- async function listAuditsAndGroups(eslint, patterns) {
945
- const rules = await listRules(eslint, patterns);
977
+ async function listAuditsAndGroups(targets) {
978
+ const rules = await listRules(targets);
946
979
  const audits = rules.map(ruleToAudit);
947
980
  const groups = [
948
981
  ...groupsFromRuleTypes(rules),
@@ -954,31 +987,17 @@ async function listAuditsAndGroups(eslint, patterns) {
954
987
  // packages/plugin-eslint/src/lib/runner/index.ts
955
988
  import { writeFile } from "node:fs/promises";
956
989
  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
990
  var WORKDIR = pluginWorkDir("eslint");
970
991
  var RUNNER_OUTPUT_PATH = join2(WORKDIR, "runner-output.json");
971
- var ESLINTRC_PATH = join2(process.cwd(), WORKDIR, ".eslintrc.json");
972
992
  var PLUGIN_CONFIG_PATH = join2(
973
993
  process.cwd(),
974
994
  WORKDIR,
975
995
  "plugin-config.json"
976
996
  );
977
- async function createRunnerConfig(scriptPath, audits, eslintrc, patterns) {
997
+ async function createRunnerConfig(scriptPath, audits, targets) {
978
998
  const config = {
979
- eslintrc,
980
- slugs: audits.map((audit) => audit.slug),
981
- patterns: toArray(patterns)
999
+ targets,
1000
+ slugs: audits.map((audit) => audit.slug)
982
1001
  };
983
1002
  await ensureDirectoryExists(dirname(PLUGIN_CONFIG_PATH));
984
1003
  await writeFile(PLUGIN_CONFIG_PATH, JSON.stringify(config));
@@ -991,14 +1010,8 @@ async function createRunnerConfig(scriptPath, audits, eslintrc, patterns) {
991
1010
 
992
1011
  // packages/plugin-eslint/src/lib/eslint-plugin.ts
993
1012
  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;
1013
+ const targets = eslintPluginConfigSchema.parse(config);
1014
+ const { audits, groups } = await listAuditsAndGroups(targets);
1002
1015
  const runnerScriptPath = join3(
1003
1016
  fileURLToPath(dirname2(import.meta.url)),
1004
1017
  "bin.js"
@@ -1013,12 +1026,7 @@ async function eslintPlugin(config) {
1013
1026
  version,
1014
1027
  audits,
1015
1028
  groups,
1016
- runner: await createRunnerConfig(
1017
- runnerScriptPath,
1018
- audits,
1019
- eslintrcPath,
1020
- patterns
1021
- )
1029
+ runner: await createRunnerConfig(runnerScriptPath, audits, targets)
1022
1030
  };
1023
1031
  }
1024
1032
 
@@ -1049,33 +1057,27 @@ async function nxProjectsToConfig(projectGraph, predicate = () => true) {
1049
1057
  const { readProjectsConfigurationFromProjectGraph } = await import("@nx/devkit");
1050
1058
  const projectsConfiguration = readProjectsConfigurationFromProjectGraph(projectGraph);
1051
1059
  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
- }))
1060
+ return Promise.all(
1061
+ projects.map(
1062
+ async (project) => ({
1063
+ eslintrc: await findCodePushupEslintrc(project) ?? getEslintConfig(project),
1064
+ patterns: [
1065
+ ...getLintFilePatterns(project),
1066
+ // HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
1067
+ // so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
1068
+ // this workaround won't be necessary once flat configs are stable (much easier to find all rules)
1069
+ `${project.sourceRoot}/*.spec.ts`,
1070
+ // jest/* and vitest/* rules
1071
+ `${project.sourceRoot}/*.cy.ts`,
1072
+ // cypress/* rules
1073
+ `${project.sourceRoot}/*.stories.ts`,
1074
+ // storybook/* rules
1075
+ `${project.sourceRoot}/.storybook/main.ts`
1076
+ // storybook/no-uninstalled-addons rule
1077
+ ]
1078
+ })
1059
1079
  )
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
- };
1080
+ );
1079
1081
  }
1080
1082
 
1081
1083
  // packages/plugin-eslint/src/lib/nx/find-all-projects.ts
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@code-pushup/eslint-plugin",
3
- "version": "0.34.0",
3
+ "version": "0.39.0",
4
4
  "license": "MIT",
5
5
  "dependencies": {
6
- "@code-pushup/utils": "0.34.0",
7
- "@code-pushup/models": "0.34.0",
6
+ "@code-pushup/utils": "0.39.0",
7
+ "@code-pushup/models": "0.39.0",
8
8
  "eslint": "^8.46.0",
9
9
  "zod": "^3.22.4"
10
10
  },
@@ -1,6 +1,6 @@
1
1
  import type { ESLint } from 'eslint';
2
2
  import { type ZodType, z } from 'zod';
3
- export declare const eslintPluginConfigSchema: z.ZodObject<{
3
+ export declare const eslintTargetSchema: z.ZodObject<{
4
4
  eslintrc: z.ZodUnion<[z.ZodString, ZodType<ESLint.ConfigData<import("eslint").Linter.RulesRecord>, z.ZodTypeDef, ESLint.ConfigData<import("eslint").Linter.RulesRecord>>]>;
5
5
  patterns: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
6
6
  }, "strip", z.ZodTypeAny, {
@@ -10,9 +10,37 @@ export declare const eslintPluginConfigSchema: z.ZodObject<{
10
10
  eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
11
11
  patterns: (string | string[]) & (string | string[] | undefined);
12
12
  }>;
13
- export type ESLintPluginConfig = z.infer<typeof eslintPluginConfigSchema>;
13
+ export type ESLintTarget = z.infer<typeof eslintTargetSchema>;
14
+ export declare const eslintPluginConfigSchema: z.ZodEffects<z.ZodUnion<[z.ZodObject<{
15
+ eslintrc: z.ZodUnion<[z.ZodString, ZodType<ESLint.ConfigData<import("eslint").Linter.RulesRecord>, z.ZodTypeDef, ESLint.ConfigData<import("eslint").Linter.RulesRecord>>]>;
16
+ patterns: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
19
+ patterns: (string | string[]) & (string | string[] | undefined);
20
+ }, {
21
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
22
+ patterns: (string | string[]) & (string | string[] | undefined);
23
+ }>, z.ZodArray<z.ZodObject<{
24
+ eslintrc: z.ZodUnion<[z.ZodString, ZodType<ESLint.ConfigData<import("eslint").Linter.RulesRecord>, z.ZodTypeDef, ESLint.ConfigData<import("eslint").Linter.RulesRecord>>]>;
25
+ patterns: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
28
+ patterns: (string | string[]) & (string | string[] | undefined);
29
+ }, {
30
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
31
+ patterns: (string | string[]) & (string | string[] | undefined);
32
+ }>, "many">]>, {
33
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
34
+ patterns: (string | string[]) & (string | string[] | undefined);
35
+ }[], {
36
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
37
+ patterns: (string | string[]) & (string | string[] | undefined);
38
+ } | {
39
+ eslintrc: (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord>) & (string | ESLint.ConfigData<import("eslint").Linter.RulesRecord> | undefined);
40
+ patterns: (string | string[]) & (string | string[] | undefined);
41
+ }[]>;
42
+ export type ESLintPluginConfig = z.input<typeof eslintPluginConfigSchema>;
14
43
  export type ESLintPluginRunnerConfig = {
15
- eslintrc: string;
44
+ targets: ESLintTarget[];
16
45
  slugs: string[];
17
- patterns: string[];
18
46
  };
@@ -1,6 +1,6 @@
1
- import type { ESLint } from 'eslint';
2
1
  import type { Audit, Group } from '@code-pushup/models';
3
- export declare function listAuditsAndGroups(eslint: ESLint, patterns: string | string[]): Promise<{
2
+ import type { ESLintTarget } from '../config';
3
+ export declare function listAuditsAndGroups(targets: ESLintTarget[]): Promise<{
4
4
  audits: Audit[];
5
5
  groups: Group[];
6
6
  }>;
@@ -1,10 +1,11 @@
1
- import type { ESLint, Rule } from 'eslint';
1
+ import type { Rule } from 'eslint';
2
+ import type { ESLintTarget } from '../config';
2
3
  export type RuleData = {
3
4
  ruleId: string;
4
5
  meta: Rule.RuleMetaData;
5
6
  options: unknown[] | undefined;
6
7
  };
7
- export declare function listRules(eslint: ESLint, patterns: string | string[]): Promise<RuleData[]>;
8
+ export declare function listRules(targets: ESLintTarget[]): Promise<RuleData[]>;
8
9
  export declare function parseRuleId(ruleId: string): {
9
10
  plugin?: string;
10
11
  name: string;
@@ -1,4 +1,4 @@
1
- import type { ESLintPluginConfig } from '../config';
1
+ import type { ESLintTarget } from '../config';
2
2
  /**
3
3
  * Finds all Nx projects in workspace and converts their lint configurations to Code PushUp ESLint plugin parameters.
4
4
  *
@@ -20,4 +20,4 @@ import type { ESLintPluginConfig } from '../config';
20
20
  *
21
21
  * @returns ESLint config and patterns, intended to be passed to {@link eslintPlugin}
22
22
  */
23
- export declare function eslintConfigFromNxProjects(): Promise<ESLintPluginConfig>;
23
+ export declare function eslintConfigFromNxProjects(): Promise<ESLintTarget[]>;
@@ -1,4 +1,4 @@
1
- import type { ESLintPluginConfig } from '../config';
1
+ import type { ESLintTarget } from '../config';
2
2
  /**
3
3
  * Accepts a target Nx projects, finds projects it depends on, and converts lint configurations to Code PushUp ESLint plugin parameters.
4
4
  *
@@ -23,4 +23,4 @@ import type { ESLintPluginConfig } from '../config';
23
23
  * @param projectName Nx project serving as main entry point
24
24
  * @returns ESLint config and patterns, intended to be passed to {@link eslintPlugin}
25
25
  */
26
- export declare function eslintConfigFromNxProject(projectName: string): Promise<ESLintPluginConfig>;
26
+ export declare function eslintConfigFromNxProject(projectName: string): Promise<ESLintTarget[]>;
@@ -1,3 +1,3 @@
1
1
  import type { ProjectConfiguration, ProjectGraph } from '@nx/devkit';
2
- import type { ESLintPluginConfig } from '../config';
3
- export declare function nxProjectsToConfig(projectGraph: ProjectGraph, predicate?: (project: ProjectConfiguration) => boolean): Promise<ESLintPluginConfig>;
2
+ import type { ESLintTarget } from '../config';
3
+ export declare function nxProjectsToConfig(projectGraph: ProjectGraph, predicate?: (project: ProjectConfiguration) => boolean): Promise<ESLintTarget[]>;
@@ -1,7 +1,7 @@
1
1
  import type { Audit, RunnerConfig } from '@code-pushup/models';
2
+ import { type ESLintTarget } from '../config';
2
3
  export declare const WORKDIR: string;
3
4
  export declare const RUNNER_OUTPUT_PATH: string;
4
- export declare const ESLINTRC_PATH: string;
5
5
  export declare const PLUGIN_CONFIG_PATH: string;
6
6
  export declare function executeRunner(): Promise<void>;
7
- export declare function createRunnerConfig(scriptPath: string, audits: Audit[], eslintrc: string, patterns: string | string[]): Promise<RunnerConfig>;
7
+ export declare function createRunnerConfig(scriptPath: string, audits: Audit[], targets: ESLintTarget[]): Promise<RunnerConfig>;
@@ -1,3 +1,3 @@
1
- import { ESLintPluginConfig } from '../config';
1
+ import type { ESLintTarget } from '../config';
2
2
  import type { LinterOutput } from './types';
3
- export declare function lint({ eslintrc, patterns, }: ESLintPluginConfig): Promise<LinterOutput>;
3
+ export declare function lint({ eslintrc, patterns, }: ESLintTarget): Promise<LinterOutput>;
@@ -1,3 +1,4 @@
1
1
  import type { AuditOutput } from '@code-pushup/models';
2
2
  import type { LinterOutput } from './types';
3
+ export declare function mergeLinterOutputs(outputs: LinterOutput[]): LinterOutput;
3
4
  export declare function lintResultsToAudits({ results, ruleOptionsPerFile, }: LinterOutput): AuditOutput[];
@@ -1,3 +1,3 @@
1
1
  import { ESLint } from 'eslint';
2
- import { ESLintPluginConfig } from './config';
3
- export declare function setupESLint(eslintrc: ESLintPluginConfig['eslintrc']): ESLint;
2
+ import type { ESLintTarget } from './config';
3
+ export declare function setupESLint(eslintrc: ESLintTarget['eslintrc']): ESLint;