@code-pushup/eslint-plugin 0.1.1 → 0.2.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/README.md +30 -0
- package/bin.js +7 -7
- package/index.js +107 -5
- package/package.json +11 -3
- package/src/index.d.ts +1 -0
- package/src/lib/nx/find-all-projects.d.ts +23 -0
- package/src/lib/nx/find-project-with-deps.d.ts +26 -0
- package/src/lib/nx/index.d.ts +2 -0
- package/src/lib/nx/projects-to-config.d.ts +3 -0
- package/src/lib/nx/traverse-graph.d.ts +2 -0
- package/src/lib/nx/utils.d.ts +4 -0
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ Detected ESLint rules are mapped to Code PushUp audits. Audit reports are calcul
|
|
|
35
35
|
Remember that Code PushUp only collects and uploads the results, it doesn't fail if errors are found.
|
|
36
36
|
So you can be more strict than in most linter setups, the idea is to set aspirational goals and track your progress.
|
|
37
37
|
|
|
38
|
+
> 💡 We recommend extending our own [`@code-pushup/eslint-config`](https://www.npmjs.com/package/@code-pushup/eslint-config). 😇
|
|
39
|
+
|
|
38
40
|
4. Add this plugin to the `plugins` array in your Code PushUp CLI config file (e.g. `code-pushup.config.js`).
|
|
39
41
|
|
|
40
42
|
Pass in the path to your ESLint config file, along with glob patterns for which files you wish to target (relative to `process.cwd()`).
|
|
@@ -51,6 +53,34 @@ Detected ESLint rules are mapped to Code PushUp audits. Audit reports are calcul
|
|
|
51
53
|
};
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
If you're using an Nx monorepo, additional helper functions are provided to simplify your configuration:
|
|
57
|
+
|
|
58
|
+
- If you wish to combine all projects in your workspace into one report, use the `eslintConfigFromNxProjects` helper:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import eslintPlugin, { eslintConfigFromNxProjects } from '@code-pushup/eslint-plugin';
|
|
62
|
+
|
|
63
|
+
export default {
|
|
64
|
+
plugins: [
|
|
65
|
+
// ...
|
|
66
|
+
await eslintPlugin(await eslintConfigFromNxProjects()),
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- If you wish to target a specific project along with other projects it depends on, use the `eslintConfigFromNxProject` helper and pass in in your project name:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import eslintPlugin, { eslintConfigFromNxProject } from '@code-pushup/eslint-plugin';
|
|
75
|
+
|
|
76
|
+
export default {
|
|
77
|
+
plugins: [
|
|
78
|
+
// ...
|
|
79
|
+
await eslintPlugin(await eslintConfigFromNxProject('<PROJECT-NAME>')),
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
```
|
|
83
|
+
|
|
54
84
|
5. (Optional) Reference audits (or groups) which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups).
|
|
55
85
|
|
|
56
86
|
Assign weights based on what influence each ESLint rule should have on the overall category score (assign weight 0 to only include as extra info, without influencing category score).
|
package/bin.js
CHANGED
|
@@ -550,13 +550,6 @@ function pluralizeToken(token, times = 0) {
|
|
|
550
550
|
}
|
|
551
551
|
|
|
552
552
|
// packages/utils/src/lib/file-system.ts
|
|
553
|
-
function toUnixPath(path, options) {
|
|
554
|
-
const unixPath = path.replace(/\\/g, "/");
|
|
555
|
-
if (options?.toRelative) {
|
|
556
|
-
return unixPath.replace(process.cwd().replace(/\\/g, "/") + "/", "");
|
|
557
|
-
}
|
|
558
|
-
return unixPath;
|
|
559
|
-
}
|
|
560
553
|
async function readTextFile(path) {
|
|
561
554
|
const buffer = await readFile(path);
|
|
562
555
|
return buffer.toString();
|
|
@@ -565,6 +558,13 @@ async function readJsonFile(path) {
|
|
|
565
558
|
const text = await readTextFile(path);
|
|
566
559
|
return JSON.parse(text);
|
|
567
560
|
}
|
|
561
|
+
function toUnixPath(path, options) {
|
|
562
|
+
const unixPath = path.replace(/\\/g, "/");
|
|
563
|
+
if (options?.toRelative) {
|
|
564
|
+
return unixPath.replace(process.cwd().replace(/\\/g, "/") + "/", "");
|
|
565
|
+
}
|
|
566
|
+
return unixPath;
|
|
567
|
+
}
|
|
568
568
|
function pluginWorkDir(slug) {
|
|
569
569
|
return join("node_modules", ".code-pushup", slug);
|
|
570
570
|
}
|
package/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// packages/plugin-eslint/src/lib/eslint-plugin.ts
|
|
2
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
2
|
+
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
3
3
|
import { dirname as dirname2, join as join3 } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
|
|
6
6
|
// packages/plugin-eslint/package.json
|
|
7
7
|
var name = "@code-pushup/eslint-plugin";
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.2.0";
|
|
9
9
|
|
|
10
10
|
// packages/plugin-eslint/src/lib/config.ts
|
|
11
11
|
import { z } from "zod";
|
|
@@ -551,6 +551,7 @@ var reportSchema = packageVersionSchema({
|
|
|
551
551
|
// packages/utils/src/lib/file-system.ts
|
|
552
552
|
import { bundleRequire } from "bundle-require";
|
|
553
553
|
import chalk from "chalk";
|
|
554
|
+
import { mkdir, readFile, readdir, stat } from "fs/promises";
|
|
554
555
|
import { join } from "path";
|
|
555
556
|
|
|
556
557
|
// packages/utils/src/lib/formatting.ts
|
|
@@ -559,6 +560,14 @@ function slugify(text) {
|
|
|
559
560
|
}
|
|
560
561
|
|
|
561
562
|
// packages/utils/src/lib/file-system.ts
|
|
563
|
+
async function fileExists(path) {
|
|
564
|
+
try {
|
|
565
|
+
const stats = await stat(path);
|
|
566
|
+
return stats.isFile();
|
|
567
|
+
} catch {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
562
571
|
function pluginWorkDir(slug) {
|
|
563
572
|
return join("node_modules", ".code-pushup", slug);
|
|
564
573
|
}
|
|
@@ -840,7 +849,7 @@ async function eslintPlugin(config) {
|
|
|
840
849
|
const eslint = setupESLint(eslintrc);
|
|
841
850
|
const { audits, groups } = await listAuditsAndGroups(eslint, patterns);
|
|
842
851
|
if (typeof eslintrc !== "string") {
|
|
843
|
-
await
|
|
852
|
+
await mkdir2(dirname2(ESLINTRC_PATH), { recursive: true });
|
|
844
853
|
await writeFile(ESLINTRC_PATH, JSON.stringify(eslintrc));
|
|
845
854
|
}
|
|
846
855
|
const eslintrcPath = typeof eslintrc === "string" ? eslintrc : ESLINTRC_PATH;
|
|
@@ -853,7 +862,7 @@ async function eslintPlugin(config) {
|
|
|
853
862
|
title: "ESLint",
|
|
854
863
|
icon: "eslint",
|
|
855
864
|
description: "Official Code PushUp ESLint plugin",
|
|
856
|
-
|
|
865
|
+
docsUrl: "https://www.npmjs.com/package/@code-pushup/eslint-plugin",
|
|
857
866
|
packageName: name,
|
|
858
867
|
version,
|
|
859
868
|
audits,
|
|
@@ -867,8 +876,101 @@ async function eslintPlugin(config) {
|
|
|
867
876
|
};
|
|
868
877
|
}
|
|
869
878
|
|
|
879
|
+
// packages/plugin-eslint/src/lib/nx/utils.ts
|
|
880
|
+
import { join as join4 } from "node:path";
|
|
881
|
+
async function findCodePushupEslintrc(project) {
|
|
882
|
+
const name2 = "code-pushup.eslintrc";
|
|
883
|
+
const extensions = ["json", "js", "cjs", "yml", "yaml"];
|
|
884
|
+
for (const ext of extensions) {
|
|
885
|
+
const filename = `./${project.root}/${name2}.${ext}`;
|
|
886
|
+
if (await fileExists(join4(process.cwd(), filename))) {
|
|
887
|
+
return filename;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
function getLintFilePatterns(project) {
|
|
893
|
+
const options = project.targets?.["lint"]?.options;
|
|
894
|
+
return options?.lintFilePatterns == null ? [] : toArray(options.lintFilePatterns);
|
|
895
|
+
}
|
|
896
|
+
function getEslintConfig(project) {
|
|
897
|
+
const options = project.targets?.["lint"]?.options;
|
|
898
|
+
return options?.eslintConfig ?? `./${project.root}/.eslintrc.json`;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// packages/plugin-eslint/src/lib/nx/projects-to-config.ts
|
|
902
|
+
async function nxProjectsToConfig(projectGraph, predicate = () => true) {
|
|
903
|
+
const { readProjectsConfigurationFromProjectGraph } = await import("@nx/devkit");
|
|
904
|
+
const projectsConfiguration = readProjectsConfigurationFromProjectGraph(projectGraph);
|
|
905
|
+
const projects = Object.values(projectsConfiguration.projects).filter((project) => "lint" in (project.targets ?? {})).filter(predicate).sort((a, b) => a.root.localeCompare(b.root));
|
|
906
|
+
const eslintConfig = {
|
|
907
|
+
root: true,
|
|
908
|
+
overrides: await Promise.all(
|
|
909
|
+
projects.map(async (project) => ({
|
|
910
|
+
files: getLintFilePatterns(project),
|
|
911
|
+
extends: await findCodePushupEslintrc(project) ?? getEslintConfig(project)
|
|
912
|
+
}))
|
|
913
|
+
)
|
|
914
|
+
};
|
|
915
|
+
const patterns = projects.flatMap((project) => [
|
|
916
|
+
...getLintFilePatterns(project),
|
|
917
|
+
// HACK: ESLint.calculateConfigForFile won't find rules included only for subsets of *.ts when globs used
|
|
918
|
+
// so we explicitly provide additional patterns used by @code-pushup/eslint-config to ensure those rules are included
|
|
919
|
+
// this workaround won't be necessary once flat configs are stable (much easier to find all rules)
|
|
920
|
+
`${project.sourceRoot}/*.spec.ts`,
|
|
921
|
+
// jest/* and vitest/* rules
|
|
922
|
+
`${project.sourceRoot}/*.cy.ts`,
|
|
923
|
+
// cypress/* rules
|
|
924
|
+
`${project.sourceRoot}/*.stories.ts`,
|
|
925
|
+
// storybook/* rules
|
|
926
|
+
`${project.sourceRoot}/.storybook/main.ts`
|
|
927
|
+
// storybook/no-uninstalled-addons rule
|
|
928
|
+
]);
|
|
929
|
+
return {
|
|
930
|
+
eslintrc: eslintConfig,
|
|
931
|
+
patterns
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// packages/plugin-eslint/src/lib/nx/find-all-projects.ts
|
|
936
|
+
async function eslintConfigFromNxProjects() {
|
|
937
|
+
const { createProjectGraphAsync } = await import("@nx/devkit");
|
|
938
|
+
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
|
|
939
|
+
return nxProjectsToConfig(projectGraph);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// packages/plugin-eslint/src/lib/nx/traverse-graph.ts
|
|
943
|
+
function findAllDependencies(entry, projectGraph) {
|
|
944
|
+
const results = /* @__PURE__ */ new Set();
|
|
945
|
+
const queue = [entry];
|
|
946
|
+
while (queue.length > 0) {
|
|
947
|
+
const source = queue.shift();
|
|
948
|
+
const dependencies = projectGraph.dependencies[source];
|
|
949
|
+
for (const { target } of dependencies ?? []) {
|
|
950
|
+
if (!results.has(target) && target !== entry) {
|
|
951
|
+
results.add(target);
|
|
952
|
+
queue.push(target);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return results;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// packages/plugin-eslint/src/lib/nx/find-project-with-deps.ts
|
|
960
|
+
async function eslintConfigFromNxProject(projectName) {
|
|
961
|
+
const { createProjectGraphAsync } = await import("@nx/devkit");
|
|
962
|
+
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
|
|
963
|
+
const dependencies = findAllDependencies(projectName, projectGraph);
|
|
964
|
+
return nxProjectsToConfig(
|
|
965
|
+
projectGraph,
|
|
966
|
+
(project) => !!project.name && (project.name === projectName || dependencies.has(project.name))
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
|
|
870
970
|
// packages/plugin-eslint/src/index.ts
|
|
871
971
|
var src_default = eslintPlugin;
|
|
872
972
|
export {
|
|
873
|
-
src_default as default
|
|
973
|
+
src_default as default,
|
|
974
|
+
eslintConfigFromNxProject,
|
|
975
|
+
eslintConfigFromNxProjects
|
|
874
976
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-pushup/eslint-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@code-pushup/utils": "*",
|
|
6
|
+
"@code-pushup/models": "*",
|
|
6
7
|
"eslint": "~8.46.0",
|
|
7
|
-
"zod": "^3.22.4"
|
|
8
|
-
|
|
8
|
+
"zod": "^3.22.4"
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"@nx/devkit": "^17.0.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependenciesMeta": {
|
|
14
|
+
"@nx/devkit": {
|
|
15
|
+
"optional": true
|
|
16
|
+
}
|
|
9
17
|
},
|
|
10
18
|
"type": "module",
|
|
11
19
|
"main": "./index.js",
|
package/src/index.d.ts
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ESLintPluginConfig } from '../config';
|
|
2
|
+
/**
|
|
3
|
+
* Finds all Nx projects in workspace and converts their lint configurations to Code PushUp ESLint plugin parameters.
|
|
4
|
+
*
|
|
5
|
+
* Use when you wish to automatically include every Nx project in a single Code PushUp project.
|
|
6
|
+
* If you prefer to only include a subset of your Nx monorepo, refer to {@link eslintConfigFromNxProject} instead.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import eslintPlugin, {
|
|
10
|
+
* eslintConfigFromNxProjects,
|
|
11
|
+
* } from '@code-pushup/eslint-plugin';
|
|
12
|
+
*
|
|
13
|
+
* export default {
|
|
14
|
+
* plugins: [
|
|
15
|
+
* await eslintPlugin(
|
|
16
|
+
* await eslintConfigFromNxProjects()
|
|
17
|
+
* )
|
|
18
|
+
* ]
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @returns ESLint config and patterns, intended to be passed to {@link eslintPlugin}
|
|
22
|
+
*/
|
|
23
|
+
export declare function eslintConfigFromNxProjects(): Promise<ESLintPluginConfig>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ESLintPluginConfig } from '../config';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts a target Nx projects, finds projects it depends on, and converts lint configurations to Code PushUp ESLint plugin parameters.
|
|
4
|
+
*
|
|
5
|
+
* Use when you wish to include a targetted subset of your Nx monorepo in your Code PushUp project.
|
|
6
|
+
* If you prefer to include all Nx projects, refer to {@link eslintConfigFromNxProjects} instead.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import eslintPlugin, {
|
|
10
|
+
* eslintConfigFromNxProject,
|
|
11
|
+
* } from '@code-pushup/eslint-plugin';
|
|
12
|
+
*
|
|
13
|
+
* const projectName = 'backoffice'; // <-- name from project.json
|
|
14
|
+
*
|
|
15
|
+
* export default {
|
|
16
|
+
* plugins: [
|
|
17
|
+
* await eslintPlugin(
|
|
18
|
+
* await eslintConfigFromNxProject(projectName)
|
|
19
|
+
* )
|
|
20
|
+
* ]
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @param projectName Nx project serving as main entry point
|
|
24
|
+
* @returns ESLint config and patterns, intended to be passed to {@link eslintPlugin}
|
|
25
|
+
*/
|
|
26
|
+
export declare function eslintConfigFromNxProject(projectName: string): Promise<ESLintPluginConfig>;
|
|
@@ -0,0 +1,3 @@
|
|
|
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>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProjectConfiguration } from '@nx/devkit';
|
|
2
|
+
export declare function findCodePushupEslintrc(project: ProjectConfiguration): Promise<string | null>;
|
|
3
|
+
export declare function getLintFilePatterns(project: ProjectConfiguration): string[];
|
|
4
|
+
export declare function getEslintConfig(project: ProjectConfiguration): string;
|