@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,480 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_NAME = void 0;
|
|
4
|
+
const devkit_1 = require("@nx/devkit");
|
|
5
|
+
const fileutils_1 = require("nx/src/utils/fileutils");
|
|
6
|
+
const graph_utils_1 = require("../utils/graph-utils");
|
|
7
|
+
const runtime_lint_utils_1 = require("../utils/runtime-lint-utils");
|
|
8
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
const ast_utils_1 = require("../utils/ast-utils");
|
|
11
|
+
const project_graph_utils_1 = require("../utils/project-graph-utils");
|
|
12
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
13
|
+
exports.RULE_NAME = 'enforce-module-boundaries';
|
|
14
|
+
exports.default = utils_2.ESLintUtils.RuleCreator(() => `https://github.com/nrwl/nx/blob/${devkit_1.NX_VERSION}/docs/generated/packages/eslint-plugin/documents/enforce-module-boundaries.md`)({
|
|
15
|
+
name: exports.RULE_NAME,
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'suggestion',
|
|
18
|
+
docs: {
|
|
19
|
+
description: `Ensure that module boundaries are respected within the monorepo`,
|
|
20
|
+
recommended: 'recommended',
|
|
21
|
+
},
|
|
22
|
+
fixable: 'code',
|
|
23
|
+
schema: [
|
|
24
|
+
{
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
enforceBuildableLibDependency: { type: 'boolean' },
|
|
28
|
+
allowCircularSelfDependency: { type: 'boolean' },
|
|
29
|
+
checkDynamicDependenciesExceptions: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: { type: 'string' },
|
|
32
|
+
},
|
|
33
|
+
banTransitiveDependencies: { type: 'boolean' },
|
|
34
|
+
checkNestedExternalImports: { type: 'boolean' },
|
|
35
|
+
allow: { type: 'array', items: { type: 'string' } },
|
|
36
|
+
buildTargets: { type: 'array', items: { type: 'string' } },
|
|
37
|
+
depConstraints: {
|
|
38
|
+
type: 'array',
|
|
39
|
+
items: {
|
|
40
|
+
oneOf: [
|
|
41
|
+
{
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
sourceTag: { type: 'string' },
|
|
45
|
+
onlyDependOnLibsWithTags: {
|
|
46
|
+
type: 'array',
|
|
47
|
+
items: { type: 'string' },
|
|
48
|
+
},
|
|
49
|
+
allowedExternalImports: {
|
|
50
|
+
type: 'array',
|
|
51
|
+
items: { type: 'string' },
|
|
52
|
+
},
|
|
53
|
+
bannedExternalImports: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
items: { type: 'string' },
|
|
56
|
+
},
|
|
57
|
+
notDependOnLibsWithTags: {
|
|
58
|
+
type: 'array',
|
|
59
|
+
items: { type: 'string' },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
additionalProperties: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: 'object',
|
|
66
|
+
properties: {
|
|
67
|
+
allSourceTags: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
items: { type: 'string' },
|
|
70
|
+
minItems: 2,
|
|
71
|
+
},
|
|
72
|
+
onlyDependOnLibsWithTags: {
|
|
73
|
+
type: 'array',
|
|
74
|
+
items: { type: 'string' },
|
|
75
|
+
},
|
|
76
|
+
allowedExternalImports: {
|
|
77
|
+
type: 'array',
|
|
78
|
+
items: { type: 'string' },
|
|
79
|
+
},
|
|
80
|
+
bannedExternalImports: {
|
|
81
|
+
type: 'array',
|
|
82
|
+
items: { type: 'string' },
|
|
83
|
+
},
|
|
84
|
+
notDependOnLibsWithTags: {
|
|
85
|
+
type: 'array',
|
|
86
|
+
items: { type: 'string' },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
additionalProperties: false,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
messages: {
|
|
99
|
+
noRelativeOrAbsoluteImportsAcrossLibraries: `Projects cannot be imported by a relative or absolute path, and must begin with a npm scope`,
|
|
100
|
+
noRelativeOrAbsoluteExternals: `External resources cannot be imported using a relative or absolute path`,
|
|
101
|
+
noCircularDependencies: `Circular dependency between "{{sourceProjectName}}" and "{{targetProjectName}}" detected: {{path}}\n\nCircular file chain:\n{{filePaths}}`,
|
|
102
|
+
noSelfCircularDependencies: `Projects should use relative imports to import from other files within the same project. Use "./path/to/file" instead of import from "{{imp}}"`,
|
|
103
|
+
noImportsOfApps: 'Imports of apps are forbidden',
|
|
104
|
+
noImportsOfE2e: 'Imports of e2e projects are forbidden',
|
|
105
|
+
noImportOfNonBuildableLibraries: 'Buildable libraries cannot import or export from non-buildable libraries',
|
|
106
|
+
noImportsOfLazyLoadedLibraries: `Static imports of lazy-loaded libraries are forbidden.\n\nLibrary "{{targetProjectName}}" is lazy-loaded in these files:\n{{filePaths}}`,
|
|
107
|
+
projectWithoutTagsCannotHaveDependencies: `A project without tags matching at least one constraint cannot depend on any libraries`,
|
|
108
|
+
bannedExternalImportsViolation: `A project tagged with "{{sourceTag}}" is not allowed to import "{{imp}}"`,
|
|
109
|
+
nestedBannedExternalImportsViolation: `A project tagged with "{{sourceTag}}" is not allowed to import "{{imp}}". Nested import found at {{childProjectName}}`,
|
|
110
|
+
noTransitiveDependencies: `Transitive dependencies are not allowed. Only packages defined in the "package.json" can be imported`,
|
|
111
|
+
onlyTagsConstraintViolation: `A project tagged with "{{sourceTag}}" can only depend on libs tagged with {{tags}}`,
|
|
112
|
+
emptyOnlyTagsConstraintViolation: 'A project tagged with "{{sourceTag}}" cannot depend on any libs with tags',
|
|
113
|
+
notTagsConstraintViolation: `A project tagged with "{{sourceTag}}" can not depend on libs tagged with {{tags}}\n\nViolation detected in:\n{{projects}}`,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
defaultOptions: [
|
|
117
|
+
{
|
|
118
|
+
allow: [],
|
|
119
|
+
buildTargets: ['build'],
|
|
120
|
+
depConstraints: [],
|
|
121
|
+
enforceBuildableLibDependency: false,
|
|
122
|
+
allowCircularSelfDependency: false,
|
|
123
|
+
checkDynamicDependenciesExceptions: [],
|
|
124
|
+
banTransitiveDependencies: false,
|
|
125
|
+
checkNestedExternalImports: false,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
create(context, [{ allow, buildTargets, depConstraints, enforceBuildableLibDependency, allowCircularSelfDependency, checkDynamicDependenciesExceptions, banTransitiveDependencies, checkNestedExternalImports, },]) {
|
|
129
|
+
/**
|
|
130
|
+
* Globally cached info about workspace
|
|
131
|
+
*/
|
|
132
|
+
const projectPath = (0, devkit_1.normalizePath)(global.projectPath || devkit_1.workspaceRoot);
|
|
133
|
+
const fileName = (0, devkit_1.normalizePath)(context.getFilename());
|
|
134
|
+
const { projectGraph, projectRootMappings, projectFileMap, targetProjectLocator, } = (0, project_graph_utils_1.readProjectGraph)(exports.RULE_NAME);
|
|
135
|
+
if (!projectGraph) {
|
|
136
|
+
return {};
|
|
137
|
+
}
|
|
138
|
+
const workspaceLayout = global.workspaceLayout;
|
|
139
|
+
function run(node) {
|
|
140
|
+
// Ignoring ExportNamedDeclarations like:
|
|
141
|
+
// export class Foo {}
|
|
142
|
+
if (!node.source) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// accept only literals because template literals have no value
|
|
146
|
+
if (node.source.type !== utils_1.AST_NODE_TYPES.Literal) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const imp = node.source.value;
|
|
150
|
+
// whitelisted import
|
|
151
|
+
if (allow.some((a) => (0, runtime_lint_utils_1.matchImportWithWildcard)(a, imp))) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const sourceFilePath = (0, runtime_lint_utils_1.getSourceFilePath)(fileName, projectPath);
|
|
155
|
+
const sourceProject = (0, runtime_lint_utils_1.findProject)(projectGraph, projectRootMappings, sourceFilePath);
|
|
156
|
+
// If source is not part of an nx workspace, return.
|
|
157
|
+
if (!sourceProject) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// check for relative and absolute imports
|
|
161
|
+
const isAbsoluteImportIntoAnotherProj = (0, runtime_lint_utils_1.isAbsoluteImportIntoAnotherProject)(imp, workspaceLayout);
|
|
162
|
+
let targetProject;
|
|
163
|
+
if (isAbsoluteImportIntoAnotherProj) {
|
|
164
|
+
targetProject = (0, runtime_lint_utils_1.findProject)(projectGraph, projectRootMappings, imp);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
targetProject = (0, runtime_lint_utils_1.getTargetProjectBasedOnRelativeImport)(imp, projectPath, projectGraph, projectRootMappings, sourceFilePath);
|
|
168
|
+
}
|
|
169
|
+
if ((targetProject && sourceProject !== targetProject) ||
|
|
170
|
+
isAbsoluteImportIntoAnotherProj) {
|
|
171
|
+
context.report({
|
|
172
|
+
node,
|
|
173
|
+
messageId: 'noRelativeOrAbsoluteImportsAcrossLibraries',
|
|
174
|
+
fix(fixer) {
|
|
175
|
+
if (targetProject) {
|
|
176
|
+
const indexTsPaths = (0, ast_utils_1.getBarrelEntryPointProjectNode)(targetProject);
|
|
177
|
+
if (indexTsPaths && indexTsPaths.length > 0) {
|
|
178
|
+
const specifiers = node.specifiers;
|
|
179
|
+
if (!specifiers || specifiers.length === 0) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const imports = specifiers
|
|
183
|
+
.filter((s) => s.type === 'ImportSpecifier')
|
|
184
|
+
.map((s) => s.imported.name);
|
|
185
|
+
// process each potential entry point and try to find the imports
|
|
186
|
+
const importsToRemap = [];
|
|
187
|
+
for (const entryPointPath of indexTsPaths) {
|
|
188
|
+
for (const importMember of imports) {
|
|
189
|
+
const importPath = (0, ast_utils_1.getRelativeImportPath)(importMember, (0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, entryPointPath.path), sourceProject.data.sourceRoot);
|
|
190
|
+
// we cannot remap, so leave it as is
|
|
191
|
+
if (importPath) {
|
|
192
|
+
importsToRemap.push({
|
|
193
|
+
member: importMember,
|
|
194
|
+
importPath: entryPointPath.importScope,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const adjustedRelativeImports = (0, runtime_lint_utils_1.groupImports)(importsToRemap);
|
|
200
|
+
if (adjustedRelativeImports !== '') {
|
|
201
|
+
return fixer.replaceTextRange(node.range, adjustedRelativeImports);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
targetProject =
|
|
210
|
+
targetProject ||
|
|
211
|
+
(0, runtime_lint_utils_1.findProjectUsingImport)(projectGraph, targetProjectLocator, sourceFilePath, imp);
|
|
212
|
+
if (!targetProject) {
|
|
213
|
+
// non-project imports cannot use relative or absolute paths
|
|
214
|
+
if ((0, fileutils_1.isRelativePath)(imp) || imp.startsWith('/')) {
|
|
215
|
+
context.report({
|
|
216
|
+
node,
|
|
217
|
+
messageId: 'noRelativeOrAbsoluteExternals',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// If target is not found (including node internals) we bail early
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// we only allow relative paths within the same project
|
|
224
|
+
// and if it's not a secondary entrypoint in an angular lib
|
|
225
|
+
if (sourceProject === targetProject) {
|
|
226
|
+
if (!allowCircularSelfDependency &&
|
|
227
|
+
!(0, fileutils_1.isRelativePath)(imp) &&
|
|
228
|
+
!(0, runtime_lint_utils_1.belongsToDifferentNgEntryPoint)(imp, sourceFilePath, sourceProject.data.root)) {
|
|
229
|
+
context.report({
|
|
230
|
+
node,
|
|
231
|
+
messageId: 'noSelfCircularDependencies',
|
|
232
|
+
data: {
|
|
233
|
+
imp,
|
|
234
|
+
},
|
|
235
|
+
fix(fixer) {
|
|
236
|
+
// imp has form of @myorg/someproject/some/path
|
|
237
|
+
const indexTsPaths = (0, ast_utils_1.getBarrelEntryPointByImportScope)(imp);
|
|
238
|
+
if (indexTsPaths.length > 0) {
|
|
239
|
+
const specifiers = node.specifiers;
|
|
240
|
+
if (!specifiers || specifiers.length === 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// imported JS functions to remap
|
|
244
|
+
const imports = specifiers
|
|
245
|
+
.filter((s) => s.type === 'ImportSpecifier')
|
|
246
|
+
.map((s) => s.imported.name);
|
|
247
|
+
// process each potential entry point and try to find the imports
|
|
248
|
+
const importsToRemap = [];
|
|
249
|
+
for (const entryPointPath of indexTsPaths) {
|
|
250
|
+
for (const importMember of imports) {
|
|
251
|
+
const importPath = (0, ast_utils_1.getRelativeImportPath)(importMember, (0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, entryPointPath), sourceProject.data.sourceRoot);
|
|
252
|
+
if (importPath) {
|
|
253
|
+
// resolve the import path
|
|
254
|
+
const relativePath = (0, path_1.relative)((0, path_1.dirname)(fileName), (0, path_1.dirname)(importPath));
|
|
255
|
+
// the string we receive from elsewhere might not have a leading './' here despite still being a relative path
|
|
256
|
+
// we'd like to ensure it's a normalized relative form starting from ./ or ../
|
|
257
|
+
const ensureRelativeForm = (path) => path.startsWith('./') || path.startsWith('../')
|
|
258
|
+
? path
|
|
259
|
+
: `./${path}`;
|
|
260
|
+
// if the string is empty, it's the current file
|
|
261
|
+
const importPathResolved = relativePath === ''
|
|
262
|
+
? `./${(0, path_1.basename)(importPath)}`
|
|
263
|
+
: ensureRelativeForm((0, devkit_1.joinPathFragments)(relativePath, (0, path_1.basename)(importPath)));
|
|
264
|
+
importsToRemap.push({
|
|
265
|
+
member: importMember,
|
|
266
|
+
// remove .ts or .tsx from the end of the file path
|
|
267
|
+
importPath: importPathResolved.replace(/.tsx?$/, ''),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const adjustedRelativeImports = (0, runtime_lint_utils_1.groupImports)(importsToRemap);
|
|
273
|
+
if (adjustedRelativeImports !== '') {
|
|
274
|
+
return fixer.replaceTextRange(node.range, adjustedRelativeImports);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// project => npm package
|
|
283
|
+
if (targetProject.type === 'npm') {
|
|
284
|
+
if (banTransitiveDependencies &&
|
|
285
|
+
!(0, runtime_lint_utils_1.isDirectDependency)(sourceProject, targetProject)) {
|
|
286
|
+
context.report({
|
|
287
|
+
node,
|
|
288
|
+
messageId: 'noTransitiveDependencies',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
const constraint = (0, runtime_lint_utils_1.hasBannedImport)(sourceProject, targetProject, depConstraints, imp);
|
|
292
|
+
if (constraint) {
|
|
293
|
+
context.report({
|
|
294
|
+
node,
|
|
295
|
+
messageId: 'bannedExternalImportsViolation',
|
|
296
|
+
data: {
|
|
297
|
+
sourceTag: (0, runtime_lint_utils_1.isComboDepConstraint)(constraint)
|
|
298
|
+
? constraint.allSourceTags.join('" and "')
|
|
299
|
+
: constraint.sourceTag,
|
|
300
|
+
imp,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// check constraints between libs and apps
|
|
307
|
+
// check for circular dependency
|
|
308
|
+
const circularPath = (0, graph_utils_1.checkCircularPath)(projectGraph, sourceProject, targetProject);
|
|
309
|
+
if (circularPath.length !== 0) {
|
|
310
|
+
const circularFilePath = (0, graph_utils_1.findFilesInCircularPath)(projectFileMap, circularPath);
|
|
311
|
+
// spacer text used for indirect dependencies when printing one line per file.
|
|
312
|
+
// without this, we can end up with a very long line that does not display well in the terminal.
|
|
313
|
+
const spacer = ' ';
|
|
314
|
+
context.report({
|
|
315
|
+
node,
|
|
316
|
+
messageId: 'noCircularDependencies',
|
|
317
|
+
data: {
|
|
318
|
+
sourceProjectName: sourceProject.name,
|
|
319
|
+
targetProjectName: targetProject.name,
|
|
320
|
+
path: circularPath.reduce((acc, v) => `${acc} -> ${v.name}`, sourceProject.name),
|
|
321
|
+
filePaths: circularFilePath
|
|
322
|
+
.map((files) => files.length > 1
|
|
323
|
+
? `[${files
|
|
324
|
+
.map((f) => `\n${spacer}${spacer}${f}`)
|
|
325
|
+
.join(',')}\n${spacer}]`
|
|
326
|
+
: files[0])
|
|
327
|
+
.reduce((acc, files) => `${acc}\n- ${files}`, `- ${sourceFilePath}`),
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
// cannot import apps
|
|
333
|
+
if (targetProject.type === 'app' && !(0, runtime_lint_utils_1.appIsMFERemote)(targetProject)) {
|
|
334
|
+
context.report({
|
|
335
|
+
node,
|
|
336
|
+
messageId: 'noImportsOfApps',
|
|
337
|
+
});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
// cannot import e2e projects
|
|
341
|
+
if (targetProject.type === 'e2e') {
|
|
342
|
+
context.report({
|
|
343
|
+
node,
|
|
344
|
+
messageId: 'noImportsOfE2e',
|
|
345
|
+
});
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
// buildable-lib is not allowed to import non-buildable-lib
|
|
349
|
+
if (enforceBuildableLibDependency === true &&
|
|
350
|
+
sourceProject.type === 'lib' &&
|
|
351
|
+
targetProject.type === 'lib') {
|
|
352
|
+
if ((0, runtime_lint_utils_1.hasBuildExecutor)(sourceProject, buildTargets) &&
|
|
353
|
+
!(0, runtime_lint_utils_1.hasBuildExecutor)(targetProject, buildTargets)) {
|
|
354
|
+
context.report({
|
|
355
|
+
node,
|
|
356
|
+
messageId: 'noImportOfNonBuildableLibraries',
|
|
357
|
+
});
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// if we import a library using loadChildren, we should not import it using es6imports
|
|
362
|
+
if (!checkDynamicDependenciesExceptions.some((a) => (0, runtime_lint_utils_1.matchImportWithWildcard)(a, imp)) &&
|
|
363
|
+
(0, runtime_lint_utils_1.hasStaticImportOfDynamicResource)(node, projectGraph, sourceProject.name, targetProject.name)) {
|
|
364
|
+
const filesWithLazyImports = (0, graph_utils_1.findFilesWithDynamicImports)(projectFileMap, sourceProject.name, targetProject.name);
|
|
365
|
+
context.report({
|
|
366
|
+
data: {
|
|
367
|
+
filePaths: filesWithLazyImports
|
|
368
|
+
.map(({ file }) => `- ${file}`)
|
|
369
|
+
.join('\n'),
|
|
370
|
+
targetProjectName: targetProject.name,
|
|
371
|
+
},
|
|
372
|
+
node,
|
|
373
|
+
messageId: 'noImportsOfLazyLoadedLibraries',
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
// check that dependency constraints are satisfied
|
|
378
|
+
if (depConstraints.length > 0) {
|
|
379
|
+
const constraints = (0, runtime_lint_utils_1.findConstraintsFor)(depConstraints, sourceProject);
|
|
380
|
+
// when no constrains found => error. Force the user to provision them.
|
|
381
|
+
if (constraints.length === 0) {
|
|
382
|
+
context.report({
|
|
383
|
+
node,
|
|
384
|
+
messageId: 'projectWithoutTagsCannotHaveDependencies',
|
|
385
|
+
});
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const transitiveExternalDeps = checkNestedExternalImports
|
|
389
|
+
? (0, runtime_lint_utils_1.findTransitiveExternalDependencies)(projectGraph, targetProject)
|
|
390
|
+
: [];
|
|
391
|
+
for (let constraint of constraints) {
|
|
392
|
+
if (constraint.onlyDependOnLibsWithTags &&
|
|
393
|
+
constraint.onlyDependOnLibsWithTags.length &&
|
|
394
|
+
(0, runtime_lint_utils_1.hasNoneOfTheseTags)(targetProject, constraint.onlyDependOnLibsWithTags)) {
|
|
395
|
+
context.report({
|
|
396
|
+
node,
|
|
397
|
+
messageId: 'onlyTagsConstraintViolation',
|
|
398
|
+
data: {
|
|
399
|
+
sourceTag: (0, runtime_lint_utils_1.isComboDepConstraint)(constraint)
|
|
400
|
+
? constraint.allSourceTags.join('" and "')
|
|
401
|
+
: constraint.sourceTag,
|
|
402
|
+
tags: (0, runtime_lint_utils_1.stringifyTags)(constraint.onlyDependOnLibsWithTags),
|
|
403
|
+
},
|
|
404
|
+
});
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (constraint.onlyDependOnLibsWithTags &&
|
|
408
|
+
constraint.onlyDependOnLibsWithTags.length === 0 &&
|
|
409
|
+
targetProject.data.tags.length !== 0) {
|
|
410
|
+
context.report({
|
|
411
|
+
node,
|
|
412
|
+
messageId: 'emptyOnlyTagsConstraintViolation',
|
|
413
|
+
data: {
|
|
414
|
+
sourceTag: (0, runtime_lint_utils_1.isComboDepConstraint)(constraint)
|
|
415
|
+
? constraint.allSourceTags.join('" and "')
|
|
416
|
+
: constraint.sourceTag,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (constraint.notDependOnLibsWithTags &&
|
|
422
|
+
constraint.notDependOnLibsWithTags.length) {
|
|
423
|
+
const projectPaths = (0, runtime_lint_utils_1.findDependenciesWithTags)(targetProject, constraint.notDependOnLibsWithTags, projectGraph);
|
|
424
|
+
if (projectPaths.length > 0) {
|
|
425
|
+
context.report({
|
|
426
|
+
node,
|
|
427
|
+
messageId: 'notTagsConstraintViolation',
|
|
428
|
+
data: {
|
|
429
|
+
sourceTag: (0, runtime_lint_utils_1.isComboDepConstraint)(constraint)
|
|
430
|
+
? constraint.allSourceTags.join('" and "')
|
|
431
|
+
: constraint.sourceTag,
|
|
432
|
+
tags: (0, runtime_lint_utils_1.stringifyTags)(constraint.notDependOnLibsWithTags),
|
|
433
|
+
projects: projectPaths
|
|
434
|
+
.map((projectPath) => `- ${projectPath.map((p) => p.name).join(' -> ')}`)
|
|
435
|
+
.join('\n'),
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (checkNestedExternalImports &&
|
|
442
|
+
constraint.bannedExternalImports &&
|
|
443
|
+
constraint.bannedExternalImports.length) {
|
|
444
|
+
const matches = (0, runtime_lint_utils_1.hasBannedDependencies)(transitiveExternalDeps, projectGraph, constraint, imp);
|
|
445
|
+
if (matches.length > 0) {
|
|
446
|
+
matches.forEach(([target, violatingSource, constraint]) => {
|
|
447
|
+
context.report({
|
|
448
|
+
node,
|
|
449
|
+
messageId: 'nestedBannedExternalImportsViolation',
|
|
450
|
+
data: {
|
|
451
|
+
sourceTag: (0, runtime_lint_utils_1.isComboDepConstraint)(constraint)
|
|
452
|
+
? constraint.allSourceTags.join('" and "')
|
|
453
|
+
: constraint.sourceTag,
|
|
454
|
+
childProjectName: violatingSource.name,
|
|
455
|
+
imp,
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
ImportDeclaration(node) {
|
|
467
|
+
run(node);
|
|
468
|
+
},
|
|
469
|
+
ImportExpression(node) {
|
|
470
|
+
run(node);
|
|
471
|
+
},
|
|
472
|
+
ExportAllDeclaration(node) {
|
|
473
|
+
run(node);
|
|
474
|
+
},
|
|
475
|
+
ExportNamedDeclaration(node) {
|
|
476
|
+
run(node);
|
|
477
|
+
},
|
|
478
|
+
};
|
|
479
|
+
},
|
|
480
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AST } from 'jsonc-eslint-parser';
|
|
2
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
3
|
+
type Options = [
|
|
4
|
+
{
|
|
5
|
+
generatorsJson?: string;
|
|
6
|
+
executorsJson?: string;
|
|
7
|
+
migrationsJson?: string;
|
|
8
|
+
packageJson?: string;
|
|
9
|
+
allowedVersionStrings: string[];
|
|
10
|
+
}
|
|
11
|
+
];
|
|
12
|
+
export type MessageIds = 'missingRequiredSchema' | 'invalidSchemaPath' | 'missingImplementation' | 'invalidImplementationPath' | 'invalidImplementationModule' | 'unableToReadImplementationExports' | 'invalidVersion' | 'missingVersion' | 'noGeneratorsOrSchematicsFound' | 'noExecutorsOrBuildersFound' | 'valueShouldBeObject';
|
|
13
|
+
export declare const RULE_NAME = "nx-plugin-checks";
|
|
14
|
+
declare const _default: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
|
|
15
|
+
export default _default;
|
|
16
|
+
export declare function checkCollectionFileNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>): void;
|
|
17
|
+
export declare function checkCollectionNode(baseNode: AST.JSONObjectExpression, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>): void;
|
|
18
|
+
export declare function validateEntry(baseNode: AST.JSONObjectExpression, key: string, mode: 'migration' | 'generator' | 'executor', context: TSESLint.RuleContext<MessageIds, Options>): void;
|
|
19
|
+
export declare function validateImplemenationNode(implementationNode: AST.JSONProperty, key: string, context: TSESLint.RuleContext<MessageIds, Options>): void;
|
|
20
|
+
export declare function validatePackageGroup(baseNode: AST.JSONObjectExpression, context: TSESLint.RuleContext<MessageIds, Options>): void;
|
|
21
|
+
export declare function validateVersionJsonExpression(node: AST.JSONExpression, context: TSESLint.RuleContext<MessageIds, Options>): string | boolean;
|