@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 +158 -42
- package/index.js +202 -200
- package/package.json +3 -3
- package/src/lib/config.d.ts +32 -4
- package/src/lib/meta/index.d.ts +2 -2
- package/src/lib/meta/rules.d.ts +3 -2
- package/src/lib/nx/find-all-projects.d.ts +2 -2
- package/src/lib/nx/find-project-with-deps.d.ts +2 -2
- package/src/lib/nx/projects-to-config.d.ts +2 -2
- package/src/lib/runner/index.d.ts +2 -2
- package/src/lib/runner/lint.d.ts +2 -2
- package/src/lib/runner/transform.d.ts +1 -0
- package/src/lib/setup.d.ts +2 -2
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
|
|
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({
|
|
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.
|
|
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
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
)
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
...filesMap,
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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(
|
|
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 =
|
|
907
|
-
var
|
|
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,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
100
|
-
date:
|
|
101
|
-
duration:
|
|
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 =
|
|
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 =
|
|
110
|
-
var urlSchema =
|
|
111
|
-
var docsUrlSchema = urlSchema.optional().or(
|
|
112
|
-
var titleSchema =
|
|
113
|
-
var scoreSchema =
|
|
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
|
|
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 =
|
|
133
|
-
var fileNameSchema =
|
|
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 =
|
|
137
|
-
var nonnegativeIntSchema =
|
|
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 =
|
|
141
|
-
const versionSchema =
|
|
142
|
-
return
|
|
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
|
|
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
|
|
148
|
+
return z.object(
|
|
164
149
|
{
|
|
165
150
|
slug: slugSchema.describe('Human-readable unique ID, e.g. "performance"'),
|
|
166
|
-
refs:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
200
|
+
import { z as z4 } from "zod";
|
|
216
201
|
|
|
217
202
|
// packages/models/src/lib/issue.ts
|
|
218
|
-
import { z as
|
|
219
|
-
var sourceFileLocationSchema =
|
|
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:
|
|
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 =
|
|
219
|
+
var issueSeveritySchema = z3.enum(["info", "warning", "error"], {
|
|
235
220
|
description: "Severity level"
|
|
236
221
|
});
|
|
237
|
-
var issueSchema =
|
|
222
|
+
var issueSchema = z3.object(
|
|
238
223
|
{
|
|
239
|
-
message:
|
|
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 =
|
|
249
|
-
var auditDetailsSchema =
|
|
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:
|
|
236
|
+
issues: z4.array(issueSchema, { description: "List of findings" })
|
|
252
237
|
},
|
|
253
238
|
{ description: "Detailed information" }
|
|
254
239
|
);
|
|
255
|
-
var auditOutputSchema =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
288
|
-
type:
|
|
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
|
-
|
|
310
|
-
isBinary:
|
|
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 =
|
|
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
|
|
346
|
-
var commitSchema =
|
|
330
|
+
import { z as z6 } from "zod";
|
|
331
|
+
var commitSchema = z6.object(
|
|
347
332
|
{
|
|
348
|
-
hash:
|
|
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:
|
|
353
|
-
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:
|
|
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
|
|
349
|
+
import { z as z12 } from "zod";
|
|
365
350
|
|
|
366
351
|
// packages/models/src/lib/persist-config.ts
|
|
367
|
-
import { z as
|
|
368
|
-
var formatSchema =
|
|
369
|
-
var persistConfigSchema =
|
|
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:
|
|
357
|
+
format: z7.array(formatSchema).optional()
|
|
373
358
|
});
|
|
374
359
|
|
|
375
360
|
// packages/models/src/lib/plugin-config.ts
|
|
376
|
-
import { z as
|
|
361
|
+
import { z as z10 } from "zod";
|
|
377
362
|
|
|
378
363
|
// packages/models/src/lib/group.ts
|
|
379
|
-
import { z as
|
|
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 =
|
|
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
|
|
425
|
-
var outputTransformSchema =
|
|
426
|
-
var runnerConfigSchema =
|
|
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:
|
|
413
|
+
command: z9.string({
|
|
429
414
|
description: "Shell command to execute"
|
|
430
415
|
}),
|
|
431
|
-
args:
|
|
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 =
|
|
440
|
-
var runnerFunctionSchema =
|
|
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
|
-
|
|
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 =
|
|
457
|
-
runner:
|
|
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
|
|
484
|
-
var uploadConfigSchema =
|
|
468
|
+
import { z as z11 } from "zod";
|
|
469
|
+
var uploadConfigSchema = z11.object({
|
|
485
470
|
server: urlSchema.describe("URL of deployed portal API"),
|
|
486
|
-
apiKey:
|
|
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:
|
|
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 =
|
|
498
|
-
plugins:
|
|
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
|
|
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
|
-
|
|
530
|
-
audits:
|
|
531
|
-
groups:
|
|
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
|
-
|
|
550
|
+
z13.object(
|
|
566
551
|
{
|
|
567
|
-
categories:
|
|
568
|
-
plugins:
|
|
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
|
|
569
|
+
import { z as z14 } from "zod";
|
|
585
570
|
function makeComparisonSchema(schema) {
|
|
586
571
|
const sharedDescription = schema.description || "Result";
|
|
587
|
-
return
|
|
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
|
|
578
|
+
return z14.object(
|
|
594
579
|
{
|
|
595
|
-
changed:
|
|
596
|
-
unchanged:
|
|
597
|
-
added:
|
|
598
|
-
removed:
|
|
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 =
|
|
588
|
+
var scorableMetaSchema = z14.object({
|
|
589
|
+
slug: slugSchema,
|
|
590
|
+
title: titleSchema,
|
|
591
|
+
docsUrl: docsUrlSchema
|
|
592
|
+
});
|
|
604
593
|
var scorableWithPluginMetaSchema = scorableMetaSchema.merge(
|
|
605
|
-
|
|
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
|
-
|
|
599
|
+
z14.object({
|
|
611
600
|
scores: makeComparisonSchema(scoreSchema).merge(
|
|
612
|
-
|
|
613
|
-
diff:
|
|
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
|
-
|
|
613
|
+
z14.object({
|
|
625
614
|
values: makeComparisonSchema(auditValueSchema).merge(
|
|
626
|
-
|
|
627
|
-
diff:
|
|
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
|
-
|
|
625
|
+
z14.object({ score: scoreSchema })
|
|
637
626
|
);
|
|
638
627
|
var groupResultSchema = scorableWithPluginMetaSchema.merge(
|
|
639
|
-
|
|
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 =
|
|
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.
|
|
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(
|
|
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
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
...acc,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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(
|
|
945
|
-
const rules = await listRules(
|
|
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,
|
|
997
|
+
async function createRunnerConfig(scriptPath, audits, targets) {
|
|
978
998
|
const config = {
|
|
979
|
-
|
|
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
|
|
995
|
-
const
|
|
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
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@code-pushup/utils": "0.
|
|
7
|
-
"@code-pushup/models": "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
|
},
|
package/src/lib/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ESLint } from 'eslint';
|
|
2
2
|
import { type ZodType, z } from 'zod';
|
|
3
|
-
export declare const
|
|
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
|
|
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
|
-
|
|
44
|
+
targets: ESLintTarget[];
|
|
16
45
|
slugs: string[];
|
|
17
|
-
patterns: string[];
|
|
18
46
|
};
|
package/src/lib/meta/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ESLint } from 'eslint';
|
|
2
1
|
import type { Audit, Group } from '@code-pushup/models';
|
|
3
|
-
|
|
2
|
+
import type { ESLintTarget } from '../config';
|
|
3
|
+
export declare function listAuditsAndGroups(targets: ESLintTarget[]): Promise<{
|
|
4
4
|
audits: Audit[];
|
|
5
5
|
groups: Group[];
|
|
6
6
|
}>;
|
package/src/lib/meta/rules.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type {
|
|
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(
|
|
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 {
|
|
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<
|
|
23
|
+
export declare function eslintConfigFromNxProjects(): Promise<ESLintTarget[]>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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<
|
|
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 {
|
|
3
|
-
export declare function nxProjectsToConfig(projectGraph: ProjectGraph, predicate?: (project: ProjectConfiguration) => boolean): Promise<
|
|
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[],
|
|
7
|
+
export declare function createRunnerConfig(scriptPath: string, audits: Audit[], targets: ESLintTarget[]): Promise<RunnerConfig>;
|
package/src/lib/runner/lint.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ESLintTarget } from '../config';
|
|
2
2
|
import type { LinterOutput } from './types';
|
|
3
|
-
export declare function lint({ eslintrc, patterns, }:
|
|
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[];
|
package/src/lib/setup.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ESLint } from 'eslint';
|
|
2
|
-
import {
|
|
3
|
-
export declare function setupESLint(eslintrc:
|
|
2
|
+
import type { ESLintTarget } from './config';
|
|
3
|
+
export declare function setupESLint(eslintrc: ESLintTarget['eslintrc']): ESLint;
|