@nx/eslint-plugin 0.0.0-pr-22179-271588f
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/LICENSE +22 -0
- package/README.md +68 -0
- package/migrations.json +18 -0
- package/package.json +54 -0
- package/src/configs/angular-template.d.ts +20 -0
- package/src/configs/angular-template.js +21 -0
- package/src/configs/angular.d.ts +25 -0
- package/src/configs/angular.js +27 -0
- package/src/configs/javascript.d.ts +62 -0
- package/src/configs/javascript.js +70 -0
- package/src/configs/react-base.d.ts +112 -0
- package/src/configs/react-base.js +141 -0
- package/src/configs/react-jsx.d.ts +71 -0
- package/src/configs/react-jsx.js +64 -0
- package/src/configs/react-tmp.d.ts +12 -0
- package/src/configs/react-tmp.js +17 -0
- package/src/configs/react-typescript.d.ts +40 -0
- package/src/configs/react-typescript.js +54 -0
- package/src/configs/typescript.d.ts +44 -0
- package/src/configs/typescript.js +53 -0
- package/src/constants.d.ts +13 -0
- package/src/constants.js +18 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +34 -0
- package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +2 -0
- package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +43 -0
- package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.d.ts +2 -0
- package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.js +29 -0
- package/src/resolve-workspace-rules.d.ts +4 -0
- package/src/resolve-workspace-rules.js +38 -0
- package/src/rules/dependency-checks.d.ts +17 -0
- package/src/rules/dependency-checks.js +240 -0
- package/src/rules/enforce-module-boundaries.d.ts +18 -0
- package/src/rules/enforce-module-boundaries.js +480 -0
- package/src/rules/nx-plugin-checks.d.ts +21 -0
- package/src/rules/nx-plugin-checks.js +392 -0
- package/src/utils/ast-utils.d.ts +12 -0
- package/src/utils/ast-utils.js +220 -0
- package/src/utils/config-utils.d.ts +6 -0
- package/src/utils/config-utils.js +18 -0
- package/src/utils/graph-utils.d.ts +6 -0
- package/src/utils/graph-utils.js +129 -0
- package/src/utils/package-json-utils.d.ts +4 -0
- package/src/utils/package-json-utils.js +37 -0
- package/src/utils/project-graph-utils.d.ts +10 -0
- package/src/utils/project-graph-utils.js +53 -0
- package/src/utils/runtime-lint-utils.d.ts +88 -0
- package/src/utils/runtime-lint-utils.js +365 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_NAME = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const semver_1 = require("semver");
|
|
6
|
+
const devkit_1 = require("@nx/devkit");
|
|
7
|
+
const find_npm_dependencies_1 = require("@nx/js/src/utils/find-npm-dependencies");
|
|
8
|
+
const project_graph_utils_1 = require("../utils/project-graph-utils");
|
|
9
|
+
const runtime_lint_utils_1 = require("../utils/runtime-lint-utils");
|
|
10
|
+
const package_json_utils_1 = require("../utils/package-json-utils");
|
|
11
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
12
|
+
exports.RULE_NAME = 'dependency-checks';
|
|
13
|
+
exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl/nx/blob/${devkit_1.NX_VERSION}/docs/generated/packages/eslint-plugin/documents/dependency-checks.md`)({
|
|
14
|
+
name: exports.RULE_NAME,
|
|
15
|
+
meta: {
|
|
16
|
+
type: 'suggestion',
|
|
17
|
+
docs: {
|
|
18
|
+
description: `Checks dependencies in project's package.json for version mismatches`,
|
|
19
|
+
recommended: 'recommended',
|
|
20
|
+
},
|
|
21
|
+
fixable: 'code',
|
|
22
|
+
schema: [
|
|
23
|
+
{
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
buildTargets: { type: 'array', items: { type: 'string' } },
|
|
27
|
+
ignoredDependencies: { type: 'array', items: { type: 'string' } },
|
|
28
|
+
ignoredFiles: { type: 'array', items: { type: 'string' } },
|
|
29
|
+
checkMissingDependencies: { type: 'boolean' },
|
|
30
|
+
checkObsoleteDependencies: { type: 'boolean' },
|
|
31
|
+
checkVersionMismatches: { type: 'boolean' },
|
|
32
|
+
includeTransitiveDependencies: { type: 'boolean' },
|
|
33
|
+
useLocalPathsForWorkspaceDependencies: { type: 'boolean' },
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
messages: {
|
|
39
|
+
missingDependency: `The "{{projectName}}" project uses the following packages, but they are missing from "{{section}}":{{packageNames}}`,
|
|
40
|
+
obsoleteDependency: `The "{{packageName}}" package is not used by "{{projectName}}" project.`,
|
|
41
|
+
versionMismatch: `The version specifier does not contain the installed version of "{{packageName}}" package: {{version}}.`,
|
|
42
|
+
missingDependencySection: `Dependency sections are missing from the "package.json" but following dependencies were detected:{{dependencies}}`,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultOptions: [
|
|
46
|
+
{
|
|
47
|
+
buildTargets: ['build'],
|
|
48
|
+
checkMissingDependencies: true,
|
|
49
|
+
checkObsoleteDependencies: true,
|
|
50
|
+
checkVersionMismatches: true,
|
|
51
|
+
ignoredDependencies: [],
|
|
52
|
+
ignoredFiles: [],
|
|
53
|
+
includeTransitiveDependencies: false,
|
|
54
|
+
useLocalPathsForWorkspaceDependencies: false,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
create(context, [{ buildTargets, ignoredDependencies, ignoredFiles, checkMissingDependencies, checkObsoleteDependencies, checkVersionMismatches, includeTransitiveDependencies, useLocalPathsForWorkspaceDependencies, },]) {
|
|
58
|
+
if (!context.parserServices.isJSON) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
const fileName = (0, devkit_1.normalizePath)(context.getFilename());
|
|
62
|
+
// support only package.json
|
|
63
|
+
if (!fileName.endsWith('/package.json')) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
const sourceFilePath = (0, runtime_lint_utils_1.getSourceFilePath)(fileName, devkit_1.workspaceRoot);
|
|
67
|
+
const { projectGraph, projectRootMappings, projectFileMap } = (0, project_graph_utils_1.readProjectGraph)(exports.RULE_NAME);
|
|
68
|
+
if (!projectGraph) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
const sourceProject = (0, runtime_lint_utils_1.findProject)(projectGraph, projectRootMappings, sourceFilePath);
|
|
72
|
+
// check if source project exists
|
|
73
|
+
if (!sourceProject) {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
// check if library has a build target
|
|
77
|
+
const buildTarget = buildTargets.find((t) => sourceProject.data.targets?.[t]);
|
|
78
|
+
if (!buildTarget) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
const rootPackageJson = (0, package_json_utils_1.getPackageJson)((0, path_1.join)(devkit_1.workspaceRoot, 'package.json'));
|
|
82
|
+
const npmDependencies = (0, find_npm_dependencies_1.findNpmDependencies)(devkit_1.workspaceRoot, sourceProject, projectGraph, projectFileMap, buildTarget, // TODO: What if child library has a build target different from the parent?
|
|
83
|
+
{
|
|
84
|
+
includeTransitiveDependencies,
|
|
85
|
+
ignoredFiles,
|
|
86
|
+
useLocalPathsForWorkspaceDependencies,
|
|
87
|
+
});
|
|
88
|
+
const expectedDependencyNames = Object.keys(npmDependencies);
|
|
89
|
+
const projPackageJsonPath = (0, path_1.join)(devkit_1.workspaceRoot, sourceProject.data.root, 'package.json');
|
|
90
|
+
const projPackageJsonDeps = (0, package_json_utils_1.getProductionDependencies)(projPackageJsonPath);
|
|
91
|
+
const rootPackageJsonDeps = (0, package_json_utils_1.getAllDependencies)(rootPackageJson);
|
|
92
|
+
function validateMissingDependencies(node) {
|
|
93
|
+
if (!checkMissingDependencies) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const missingDeps = expectedDependencyNames.filter((d) => !projPackageJsonDeps[d] && !ignoredDependencies.includes(d));
|
|
97
|
+
if (missingDeps.length) {
|
|
98
|
+
context.report({
|
|
99
|
+
node: node,
|
|
100
|
+
messageId: 'missingDependency',
|
|
101
|
+
data: {
|
|
102
|
+
packageNames: missingDeps.map((d) => `\n - ${d}`).join(''),
|
|
103
|
+
section: node.key.value,
|
|
104
|
+
projectName: sourceProject.name,
|
|
105
|
+
},
|
|
106
|
+
fix(fixer) {
|
|
107
|
+
missingDeps.forEach((d) => {
|
|
108
|
+
projPackageJsonDeps[d] =
|
|
109
|
+
rootPackageJsonDeps[d] || npmDependencies[d];
|
|
110
|
+
});
|
|
111
|
+
const deps = node.value.properties;
|
|
112
|
+
const mappedDeps = missingDeps
|
|
113
|
+
.map((d) => `\n "${d}": "${projPackageJsonDeps[d]}"`)
|
|
114
|
+
.join(',');
|
|
115
|
+
if (deps.length) {
|
|
116
|
+
return fixer.insertTextAfter(deps[deps.length - 1], `,${mappedDeps}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
return fixer.insertTextAfterRange([node.value.range[0] + 1, node.value.range[1] - 1], `${mappedDeps}\n `);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function validateVersionMatchesInstalled(node, packageName, packageRange) {
|
|
126
|
+
if (!checkVersionMismatches) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (npmDependencies[packageName].startsWith('file:') ||
|
|
130
|
+
packageRange.startsWith('file:') ||
|
|
131
|
+
npmDependencies[packageName] === '*' ||
|
|
132
|
+
packageRange === '*' ||
|
|
133
|
+
(0, semver_1.satisfies)(npmDependencies[packageName], packageRange, {
|
|
134
|
+
includePrerelease: true,
|
|
135
|
+
})) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
context.report({
|
|
139
|
+
node: node,
|
|
140
|
+
messageId: 'versionMismatch',
|
|
141
|
+
data: {
|
|
142
|
+
packageName: packageName,
|
|
143
|
+
version: npmDependencies[packageName],
|
|
144
|
+
},
|
|
145
|
+
fix: (fixer) => fixer.replaceText(node, `"${packageName}": "${rootPackageJsonDeps[packageName] || npmDependencies[packageName]}"`),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function reportObsoleteDependency(node, packageName) {
|
|
149
|
+
if (!checkObsoleteDependencies) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
context.report({
|
|
153
|
+
node: node,
|
|
154
|
+
messageId: 'obsoleteDependency',
|
|
155
|
+
data: { packageName: packageName, projectName: sourceProject.name },
|
|
156
|
+
fix: (fixer) => {
|
|
157
|
+
const isLastProperty = node.parent.properties[node.parent.properties.length - 1] === node;
|
|
158
|
+
const index = node.parent.properties.findIndex((n) => n === node);
|
|
159
|
+
if (index > 0) {
|
|
160
|
+
const previousNode = node.parent.properties[index - 1];
|
|
161
|
+
return fixer.removeRange([
|
|
162
|
+
previousNode.range[1] + (isLastProperty ? 0 : 1),
|
|
163
|
+
node.range[1] + (isLastProperty ? 0 : 1),
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const parent = node.parent;
|
|
168
|
+
// it's the only property
|
|
169
|
+
if (isLastProperty) {
|
|
170
|
+
return fixer.removeRange([
|
|
171
|
+
parent.range[0] + 1,
|
|
172
|
+
parent.range[1] - 1,
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return fixer.removeRange([
|
|
177
|
+
parent.range[0] + 1,
|
|
178
|
+
node.range[1] + 1,
|
|
179
|
+
]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function validateDependenciesSectionExistance(node) {
|
|
186
|
+
if (!expectedDependencyNames.length ||
|
|
187
|
+
!expectedDependencyNames.some((d) => !ignoredDependencies.includes(d))) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!node.properties ||
|
|
191
|
+
!node.properties.some((p) => ['dependencies', 'peerDependencies', 'optionalDependencies'].includes(p.key.value))) {
|
|
192
|
+
context.report({
|
|
193
|
+
node: node,
|
|
194
|
+
messageId: 'missingDependencySection',
|
|
195
|
+
data: {
|
|
196
|
+
dependencies: expectedDependencyNames
|
|
197
|
+
.map((d) => `\n- "${d}"`)
|
|
198
|
+
.join(),
|
|
199
|
+
},
|
|
200
|
+
fix: (fixer) => {
|
|
201
|
+
expectedDependencyNames.sort().reduce((acc, d) => {
|
|
202
|
+
acc[d] = rootPackageJsonDeps[d] || npmDependencies[d];
|
|
203
|
+
return acc;
|
|
204
|
+
}, projPackageJsonDeps);
|
|
205
|
+
const dependencies = Object.keys(projPackageJsonDeps)
|
|
206
|
+
.map((d) => `\n "${d}": "${projPackageJsonDeps[d]}"`)
|
|
207
|
+
.join(',');
|
|
208
|
+
if (!node.properties.length) {
|
|
209
|
+
return fixer.replaceText(node, `{\n "dependencies": {${dependencies}\n }\n}`);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
return fixer.insertTextAfter(node.properties[node.properties.length - 1], `,\n "dependencies": {${dependencies}\n }`);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
['JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=/^(peer|optional)?dependencies$/i]'](node) {
|
|
220
|
+
validateMissingDependencies(node);
|
|
221
|
+
},
|
|
222
|
+
['JSONExpressionStatement > JSONObjectExpression > JSONProperty[key.value=/^(peer|optional)?dependencies$/i] > JSONObjectExpression > JSONProperty'](node) {
|
|
223
|
+
const packageName = node.key.value;
|
|
224
|
+
const packageRange = node.value.value;
|
|
225
|
+
if (ignoredDependencies.includes(packageName)) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (expectedDependencyNames.includes(packageName)) {
|
|
229
|
+
validateVersionMatchesInstalled(node, packageName, packageRange);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
reportObsoleteDependency(node, packageName);
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
['JSONExpressionStatement > JSONObjectExpression'](node) {
|
|
236
|
+
validateDependenciesSectionExistance(node);
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DepConstraint } from '../utils/runtime-lint-utils';
|
|
2
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
3
|
+
type Options = [
|
|
4
|
+
{
|
|
5
|
+
allow: string[];
|
|
6
|
+
buildTargets: string[];
|
|
7
|
+
depConstraints: DepConstraint[];
|
|
8
|
+
enforceBuildableLibDependency: boolean;
|
|
9
|
+
allowCircularSelfDependency: boolean;
|
|
10
|
+
checkDynamicDependenciesExceptions: string[];
|
|
11
|
+
banTransitiveDependencies: boolean;
|
|
12
|
+
checkNestedExternalImports: boolean;
|
|
13
|
+
}
|
|
14
|
+
];
|
|
15
|
+
export type MessageIds = 'noRelativeOrAbsoluteImportsAcrossLibraries' | 'noRelativeOrAbsoluteExternals' | 'noSelfCircularDependencies' | 'noCircularDependencies' | 'noImportsOfApps' | 'noImportsOfE2e' | 'noImportOfNonBuildableLibraries' | 'noImportsOfLazyLoadedLibraries' | 'projectWithoutTagsCannotHaveDependencies' | 'bannedExternalImportsViolation' | 'nestedBannedExternalImportsViolation' | 'noTransitiveDependencies' | 'onlyTagsConstraintViolation' | 'emptyOnlyTagsConstraintViolation' | 'notTagsConstraintViolation';
|
|
16
|
+
export declare const RULE_NAME = "enforce-module-boundaries";
|
|
17
|
+
declare const _default: ESLintUtils.RuleModule<MessageIds, Options, ESLintUtils.RuleListener>;
|
|
18
|
+
export default _default;
|