@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.
Files changed (48) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +68 -0
  3. package/migrations.json +18 -0
  4. package/package.json +54 -0
  5. package/src/configs/angular-template.d.ts +20 -0
  6. package/src/configs/angular-template.js +21 -0
  7. package/src/configs/angular.d.ts +25 -0
  8. package/src/configs/angular.js +27 -0
  9. package/src/configs/javascript.d.ts +62 -0
  10. package/src/configs/javascript.js +70 -0
  11. package/src/configs/react-base.d.ts +112 -0
  12. package/src/configs/react-base.js +141 -0
  13. package/src/configs/react-jsx.d.ts +71 -0
  14. package/src/configs/react-jsx.js +64 -0
  15. package/src/configs/react-tmp.d.ts +12 -0
  16. package/src/configs/react-tmp.js +17 -0
  17. package/src/configs/react-typescript.d.ts +40 -0
  18. package/src/configs/react-typescript.js +54 -0
  19. package/src/configs/typescript.d.ts +44 -0
  20. package/src/configs/typescript.js +53 -0
  21. package/src/constants.d.ts +13 -0
  22. package/src/constants.js +18 -0
  23. package/src/index.d.ts +1 -0
  24. package/src/index.js +34 -0
  25. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.d.ts +2 -0
  26. package/src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages.js +43 -0
  27. package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.d.ts +2 -0
  28. package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.js +29 -0
  29. package/src/resolve-workspace-rules.d.ts +4 -0
  30. package/src/resolve-workspace-rules.js +38 -0
  31. package/src/rules/dependency-checks.d.ts +17 -0
  32. package/src/rules/dependency-checks.js +240 -0
  33. package/src/rules/enforce-module-boundaries.d.ts +18 -0
  34. package/src/rules/enforce-module-boundaries.js +480 -0
  35. package/src/rules/nx-plugin-checks.d.ts +21 -0
  36. package/src/rules/nx-plugin-checks.js +392 -0
  37. package/src/utils/ast-utils.d.ts +12 -0
  38. package/src/utils/ast-utils.js +220 -0
  39. package/src/utils/config-utils.d.ts +6 -0
  40. package/src/utils/config-utils.js +18 -0
  41. package/src/utils/graph-utils.d.ts +6 -0
  42. package/src/utils/graph-utils.js +129 -0
  43. package/src/utils/package-json-utils.d.ts +4 -0
  44. package/src/utils/package-json-utils.js +37 -0
  45. package/src/utils/project-graph-utils.d.ts +10 -0
  46. package/src/utils/project-graph-utils.js +53 -0
  47. package/src/utils/runtime-lint-utils.d.ts +88 -0
  48. package/src/utils/runtime-lint-utils.js +365 -0
@@ -0,0 +1,365 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appIsMFERemote = exports.belongsToDifferentNgEntryPoint = exports.groupImports = exports.isTerminalRun = exports.hasBuildExecutor = exports.isDirectDependency = exports.hasBannedDependencies = exports.findTransitiveExternalDependencies = exports.hasBannedImport = exports.getSourceFilePath = exports.hasStaticImportOfDynamicResource = exports.findConstraintsFor = exports.findProjectUsingImport = exports.isAbsoluteImportIntoAnotherProject = exports.findProject = exports.getTargetProjectBasedOnRelativeImport = exports.isRelative = exports.matchImportWithWildcard = exports.findDependenciesWithTags = exports.isComboDepConstraint = exports.hasNoneOfTheseTags = exports.stringifyTags = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const path_1 = require("path");
7
+ const devkit_1 = require("@nx/devkit");
8
+ const graph_utils_1 = require("./graph-utils");
9
+ const fileutils_1 = require("nx/src/utils/fileutils");
10
+ const find_project_for_path_1 = require("nx/src/project-graph/utils/find-project-for-path");
11
+ const js_1 = require("@nx/js");
12
+ const internal_1 = require("@nx/js/src/internal");
13
+ const utils_1 = require("@typescript-eslint/utils");
14
+ function stringifyTags(tags) {
15
+ return tags.map((t) => `"${t}"`).join(', ');
16
+ }
17
+ exports.stringifyTags = stringifyTags;
18
+ function hasNoneOfTheseTags(proj, tags) {
19
+ return tags.filter((tag) => hasTag(proj, tag)).length === 0;
20
+ }
21
+ exports.hasNoneOfTheseTags = hasNoneOfTheseTags;
22
+ function isComboDepConstraint(depConstraint) {
23
+ return !!depConstraint.allSourceTags;
24
+ }
25
+ exports.isComboDepConstraint = isComboDepConstraint;
26
+ /**
27
+ * Check if any of the given tags is included in the project
28
+ * @param proj ProjectGraphProjectNode
29
+ * @param tags
30
+ * @returns
31
+ */
32
+ function findDependenciesWithTags(targetProject, tags, graph) {
33
+ // find all reachable projects that have one of the tags and
34
+ // are reacheable from the targetProject (including self)
35
+ const allReachableProjects = Object.keys(graph.nodes).filter((projectName) => (0, graph_utils_1.pathExists)(graph, targetProject.name, projectName) &&
36
+ tags.some((tag) => hasTag(graph.nodes[projectName], tag)));
37
+ // return path from targetProject to reachable project
38
+ return allReachableProjects.map((project) => targetProject.name === project
39
+ ? [targetProject]
40
+ : (0, graph_utils_1.getPath)(graph, targetProject.name, project));
41
+ }
42
+ exports.findDependenciesWithTags = findDependenciesWithTags;
43
+ const regexMap = new Map();
44
+ function hasTag(proj, tag) {
45
+ if (tag === '*')
46
+ return true;
47
+ // if the tag is a regex, check if the project matches the regex
48
+ if (tag.startsWith('/') && tag.endsWith('/')) {
49
+ let regex;
50
+ if (regexMap.has(tag)) {
51
+ regex = regexMap.get(tag);
52
+ }
53
+ else {
54
+ regex = new RegExp(tag.substring(1, tag.length - 1));
55
+ regexMap.set(tag, regex);
56
+ }
57
+ return (proj.data.tags || []).some((t) => regex.test(t));
58
+ }
59
+ // if the tag is a glob, check if the project matches the glob prefix
60
+ if (tag.includes('*')) {
61
+ const regex = mapGlobToRegExp(tag);
62
+ return (proj.data.tags || []).some((t) => regex.test(t));
63
+ }
64
+ return (proj.data.tags || []).indexOf(tag) > -1;
65
+ }
66
+ function matchImportWithWildcard(
67
+ // This may or may not contain wildcards ("*")
68
+ allowableImport, extractedImport) {
69
+ if (allowableImport.endsWith('/**')) {
70
+ const prefix = allowableImport.substring(0, allowableImport.length - 2);
71
+ return extractedImport.startsWith(prefix);
72
+ }
73
+ else if (allowableImport.endsWith('/*')) {
74
+ const prefix = allowableImport.substring(0, allowableImport.length - 1);
75
+ if (!extractedImport.startsWith(prefix))
76
+ return false;
77
+ return extractedImport.substring(prefix.length).indexOf('/') === -1;
78
+ }
79
+ else if (allowableImport.indexOf('/**/') > -1) {
80
+ const [prefix, suffix] = allowableImport.split('/**/');
81
+ return (extractedImport.startsWith(prefix) && extractedImport.endsWith(suffix));
82
+ }
83
+ else {
84
+ return new RegExp(allowableImport).test(extractedImport);
85
+ }
86
+ }
87
+ exports.matchImportWithWildcard = matchImportWithWildcard;
88
+ function isRelative(s) {
89
+ return s.startsWith('./') || s.startsWith('../');
90
+ }
91
+ exports.isRelative = isRelative;
92
+ function getTargetProjectBasedOnRelativeImport(imp, projectPath, projectGraph, projectRootMappings, sourceFilePath) {
93
+ if (!isRelative(imp)) {
94
+ return undefined;
95
+ }
96
+ const sourceDir = path.join(projectPath, path.dirname(sourceFilePath));
97
+ const targetFile = (0, devkit_1.normalizePath)(path.resolve(sourceDir, imp)).substring(projectPath.length + 1);
98
+ return findProject(projectGraph, projectRootMappings, targetFile);
99
+ }
100
+ exports.getTargetProjectBasedOnRelativeImport = getTargetProjectBasedOnRelativeImport;
101
+ function findProject(projectGraph, projectRootMappings, sourceFilePath) {
102
+ return projectGraph.nodes[(0, find_project_for_path_1.findProjectForPath)(sourceFilePath, projectRootMappings)];
103
+ }
104
+ exports.findProject = findProject;
105
+ function isAbsoluteImportIntoAnotherProject(imp, workspaceLayout = { libsDir: 'libs', appsDir: 'apps' }) {
106
+ return (imp.startsWith(`${workspaceLayout.libsDir}/`) ||
107
+ imp.startsWith(`/${workspaceLayout.libsDir}/`) ||
108
+ imp.startsWith(`${workspaceLayout.appsDir}/`) ||
109
+ imp.startsWith(`/${workspaceLayout.appsDir}/`));
110
+ }
111
+ exports.isAbsoluteImportIntoAnotherProject = isAbsoluteImportIntoAnotherProject;
112
+ function findProjectUsingImport(projectGraph, targetProjectLocator, filePath, imp) {
113
+ const target = targetProjectLocator.findProjectWithImport(imp, filePath);
114
+ return projectGraph.nodes[target] || projectGraph.externalNodes?.[target];
115
+ }
116
+ exports.findProjectUsingImport = findProjectUsingImport;
117
+ function findConstraintsFor(depConstraints, sourceProject) {
118
+ return depConstraints.filter((f) => {
119
+ if (isComboDepConstraint(f)) {
120
+ return f.allSourceTags.every((tag) => hasTag(sourceProject, tag));
121
+ }
122
+ else {
123
+ return hasTag(sourceProject, f.sourceTag);
124
+ }
125
+ });
126
+ }
127
+ exports.findConstraintsFor = findConstraintsFor;
128
+ function hasStaticImportOfDynamicResource(node, graph, sourceProjectName, targetProjectName) {
129
+ if (node.type !== utils_1.AST_NODE_TYPES.ImportDeclaration ||
130
+ node.importKind === 'type') {
131
+ return false;
132
+ }
133
+ return onlyLoadChildren(graph, sourceProjectName, targetProjectName, []);
134
+ }
135
+ exports.hasStaticImportOfDynamicResource = hasStaticImportOfDynamicResource;
136
+ function onlyLoadChildren(graph, sourceProjectName, targetProjectName, visited) {
137
+ if (visited.indexOf(sourceProjectName) > -1) {
138
+ return false;
139
+ }
140
+ return ((graph.dependencies[sourceProjectName] || []).filter((d) => {
141
+ if (d.type !== devkit_1.DependencyType.dynamic) {
142
+ return false;
143
+ }
144
+ if (d.target === targetProjectName) {
145
+ return true;
146
+ }
147
+ return onlyLoadChildren(graph, d.target, targetProjectName, [
148
+ ...visited,
149
+ sourceProjectName,
150
+ ]);
151
+ }).length > 0);
152
+ }
153
+ function getSourceFilePath(sourceFileName, projectPath) {
154
+ const normalizedProjectPath = (0, devkit_1.normalizePath)(projectPath);
155
+ const normalizedSourceFileName = (0, devkit_1.normalizePath)(sourceFileName);
156
+ return normalizedSourceFileName.slice(normalizedProjectPath.length + 1);
157
+ }
158
+ exports.getSourceFilePath = getSourceFilePath;
159
+ /**
160
+ * Find constraint (if any) that explicitly banns the given target npm project
161
+ * @param externalProject
162
+ * @param depConstraints
163
+ * @returns
164
+ */
165
+ function isConstraintBanningProject(externalProject, constraint, imp) {
166
+ const { allowedExternalImports, bannedExternalImports } = constraint;
167
+ const { packageName } = externalProject.data;
168
+ if (imp !== packageName && !imp.startsWith(`${packageName}/`)) {
169
+ return false;
170
+ }
171
+ /* Check if import is banned... */
172
+ if (bannedExternalImports?.some((importDefinition) => mapGlobToRegExp(importDefinition).test(imp))) {
173
+ return true;
174
+ }
175
+ /* ... then check if there is a whitelist and if there is a match in the whitelist. */
176
+ return allowedExternalImports?.every((importDefinition) => !imp.startsWith(packageName) ||
177
+ !mapGlobToRegExp(importDefinition).test(imp));
178
+ }
179
+ function hasBannedImport(source, target, depConstraints, imp) {
180
+ // return those constraints that match source project
181
+ depConstraints = depConstraints.filter((c) => {
182
+ let tags = [];
183
+ if (isComboDepConstraint(c)) {
184
+ tags = c.allSourceTags;
185
+ }
186
+ else {
187
+ tags = [c.sourceTag];
188
+ }
189
+ return tags.every((t) => hasTag(source, t));
190
+ });
191
+ return depConstraints.find((constraint) => isConstraintBanningProject(target, constraint, imp));
192
+ }
193
+ exports.hasBannedImport = hasBannedImport;
194
+ /**
195
+ * Find all unique (transitive) external dependencies of given project
196
+ * @param graph
197
+ * @param source
198
+ * @returns
199
+ */
200
+ function findTransitiveExternalDependencies(graph, source) {
201
+ if (!graph.externalNodes) {
202
+ return [];
203
+ }
204
+ const allReachableProjects = [];
205
+ const allProjects = Object.keys(graph.nodes);
206
+ for (let i = 0; i < allProjects.length; i++) {
207
+ if ((0, graph_utils_1.pathExists)(graph, source.name, allProjects[i])) {
208
+ allReachableProjects.push(allProjects[i]);
209
+ }
210
+ }
211
+ const externalDependencies = [];
212
+ for (let i = 0; i < allReachableProjects.length; i++) {
213
+ const dependencies = graph.dependencies[allReachableProjects[i]];
214
+ if (dependencies) {
215
+ for (let d = 0; d < dependencies.length; d++) {
216
+ const dependency = dependencies[d];
217
+ if (graph.externalNodes[dependency.target]) {
218
+ externalDependencies.push(dependency);
219
+ }
220
+ }
221
+ }
222
+ }
223
+ return externalDependencies;
224
+ }
225
+ exports.findTransitiveExternalDependencies = findTransitiveExternalDependencies;
226
+ /**
227
+ * Check if
228
+ * @param externalDependencies
229
+ * @param graph
230
+ * @param depConstraint
231
+ * @returns
232
+ */
233
+ function hasBannedDependencies(externalDependencies, graph, depConstraint, imp) {
234
+ return externalDependencies
235
+ .filter((dependency) => isConstraintBanningProject(graph.externalNodes[dependency.target], depConstraint, imp))
236
+ .map((dep) => [
237
+ graph.externalNodes[dep.target],
238
+ graph.nodes[dep.source],
239
+ depConstraint,
240
+ ]);
241
+ }
242
+ exports.hasBannedDependencies = hasBannedDependencies;
243
+ function isDirectDependency(source, target) {
244
+ return (packageExistsInPackageJson(target.data.packageName, '.') ||
245
+ packageExistsInPackageJson(target.data.packageName, source.data.root));
246
+ }
247
+ exports.isDirectDependency = isDirectDependency;
248
+ function packageExistsInPackageJson(packageName, projectRoot) {
249
+ const content = (0, fileutils_1.readFileIfExisting)((0, path_1.join)(devkit_1.workspaceRoot, projectRoot, 'package.json'));
250
+ if (content) {
251
+ const { dependencies, devDependencies, peerDependencies } = (0, devkit_1.parseJson)(content);
252
+ if (dependencies && dependencies[packageName]) {
253
+ return true;
254
+ }
255
+ if (peerDependencies && peerDependencies[packageName]) {
256
+ return true;
257
+ }
258
+ if (devDependencies && devDependencies[packageName]) {
259
+ return true;
260
+ }
261
+ }
262
+ return false;
263
+ }
264
+ /**
265
+ * Maps import with wildcards to regex pattern
266
+ * @param importDefinition
267
+ * @returns
268
+ */
269
+ function mapGlobToRegExp(importDefinition) {
270
+ // we replace all instances of `*`, `**..*` and `.*` with `.*`
271
+ const mappedWildcards = importDefinition.split(/(?:\.\*)|\*+/).join('.*');
272
+ return new RegExp(`^${new RegExp(mappedWildcards).source}$`);
273
+ }
274
+ /**
275
+ * Verifies whether the given node has a builder target
276
+ * @param projectGraph the node to verify
277
+ * @param buildTargets the list of targets to check for
278
+ */
279
+ function hasBuildExecutor(projectGraph, buildTargets = ['build']) {
280
+ return (projectGraph.data.targets &&
281
+ buildTargets.some((target) => projectGraph.data.targets[target] &&
282
+ projectGraph.data.targets[target].executor !== ''));
283
+ }
284
+ exports.hasBuildExecutor = hasBuildExecutor;
285
+ const ESLINT_REGEX = /node_modules.*[\/\\]eslint(?:\.js)?$/;
286
+ const JEST_REGEX = /node_modules\/.bin\/jest$/; // when we run unit tests in jest
287
+ const NRWL_CLI_REGEX = /nx[\/\\]bin[\/\\]run-executor\.js$/;
288
+ function isTerminalRun() {
289
+ return (process.argv.length > 1 &&
290
+ (!!process.argv[1].match(NRWL_CLI_REGEX) ||
291
+ !!process.argv[1].match(JEST_REGEX) ||
292
+ !!process.argv[1].match(ESLINT_REGEX) ||
293
+ !!process.argv[1].endsWith('/bin/jest.js')));
294
+ }
295
+ exports.isTerminalRun = isTerminalRun;
296
+ /**
297
+ * Takes an array of imports and tries to group them, so rather than having
298
+ * `import { A } from './some-location'` and `import { B } from './some-location'` you get
299
+ * `import { A, B } from './some-location'`
300
+ * @param importsToRemap
301
+ * @returns
302
+ */
303
+ function groupImports(importsToRemap) {
304
+ const importsToRemapGrouped = importsToRemap.reduce((acc, curr) => {
305
+ const existing = acc.find((i) => i.importPath === curr.importPath && i.member !== curr.member);
306
+ if (existing) {
307
+ if (existing.member) {
308
+ existing.member += `, ${curr.member}`;
309
+ }
310
+ }
311
+ else {
312
+ acc.push({
313
+ importPath: curr.importPath,
314
+ member: curr.member,
315
+ });
316
+ }
317
+ return acc;
318
+ }, []);
319
+ return importsToRemapGrouped
320
+ .map((entry) => `import { ${entry.member} } from '${entry.importPath}';`)
321
+ .join('\n');
322
+ }
323
+ exports.groupImports = groupImports;
324
+ /**
325
+ * Checks if source file belongs to a secondary entry point different than the import one
326
+ */
327
+ function belongsToDifferentNgEntryPoint(importExpr, filePath, projectRoot) {
328
+ const resolvedImportFile = (0, internal_1.resolveModuleByImport)(importExpr, filePath, // not strictly necessary, but speeds up resolution
329
+ (0, path_1.join)(devkit_1.workspaceRoot, (0, js_1.getRootTsConfigFileName)()));
330
+ if (!resolvedImportFile) {
331
+ return false;
332
+ }
333
+ const importEntryPoint = getAngularEntryPoint(resolvedImportFile, projectRoot);
334
+ const srcEntryPoint = getAngularEntryPoint(filePath, projectRoot);
335
+ // check if the entry point of import expression is different than the source file's entry point
336
+ return importEntryPoint !== srcEntryPoint;
337
+ }
338
+ exports.belongsToDifferentNgEntryPoint = belongsToDifferentNgEntryPoint;
339
+ function getAngularEntryPoint(file, projectRoot) {
340
+ let parent = (0, devkit_1.joinPathFragments)(file, '../');
341
+ while (parent !== `${projectRoot}/`) {
342
+ // we need to find closest existing ng-package.json
343
+ // in order to determine if the file matches the secondary entry point
344
+ const ngPackageContent = (0, fileutils_1.readFileIfExisting)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, parent, 'ng-package.json'));
345
+ if (ngPackageContent) {
346
+ // https://github.com/ng-packagr/ng-packagr/blob/23c718d04eea85e015b4c261310b7bd0c39e5311/src/ng-package.schema.json#L54
347
+ const entryFile = (0, devkit_1.parseJson)(ngPackageContent)?.lib?.entryFile;
348
+ return (0, devkit_1.joinPathFragments)(parent, entryFile);
349
+ }
350
+ parent = (0, devkit_1.joinPathFragments)(parent, '../');
351
+ }
352
+ return undefined;
353
+ }
354
+ /**
355
+ * Returns true if the given project contains MFE config with "exposes:" section
356
+ */
357
+ function appIsMFERemote(project) {
358
+ const mfeConfig = (0, fileutils_1.readFileIfExisting)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, project.data.root, 'module-federation.config.js')) ||
359
+ (0, fileutils_1.readFileIfExisting)((0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, project.data.root, 'module-federation.config.ts'));
360
+ if (mfeConfig) {
361
+ return !!mfeConfig.match(/('|")?exposes('|")?:/);
362
+ }
363
+ return false;
364
+ }
365
+ exports.appIsMFERemote = appIsMFERemote;