@nx/eslint-plugin 21.0.0-beta.1 → 21.0.0-beta.11

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/migrations.json CHANGED
@@ -1,11 +1,5 @@
1
1
  {
2
2
  "generators": {
3
- "update-17-2-6-rename-workspace-rules": {
4
- "cli": "nx",
5
- "version": "17.2.6-beta.1",
6
- "description": "Rename workspace rules from @nx/workspace/name to @nx/workspace-name",
7
- "implementation": "./src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules"
8
- },
9
3
  "update-19-1-0-rename-no-extra-semi": {
10
4
  "cli": "nx",
11
5
  "version": "19.1.0-beta.6",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/eslint-plugin",
3
- "version": "21.0.0-beta.1",
3
+ "version": "21.0.0-beta.11",
4
4
  "private": false,
5
5
  "description": "The eslint-plugin package is an ESLint plugin that contains a collection of recommended ESLint rule configurations which you can extend from in your own ESLint configs, as well as an Nx-specific lint rule called enforce-module-boundaries.",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "homepage": "https://nx.dev",
27
27
  "peerDependencies": {
28
28
  "@typescript-eslint/parser": "^6.13.2 || ^7.0.0 || ^8.0.0",
29
- "eslint-config-prettier": "^9.0.0"
29
+ "eslint-config-prettier": "^10.0.0"
30
30
  },
31
31
  "peerDependenciesMeta": {
32
32
  "eslint-config-prettier": {
@@ -34,8 +34,8 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
- "@nx/devkit": "21.0.0-beta.1",
38
- "@nx/js": "21.0.0-beta.1",
37
+ "@nx/devkit": "21.0.0-beta.11",
38
+ "@nx/js": "21.0.0-beta.11",
39
39
  "@typescript-eslint/type-utils": "^8.0.0",
40
40
  "@typescript-eslint/utils": "^8.0.0",
41
41
  "chalk": "^4.1.0",
@@ -27,7 +27,7 @@ exports.default = typescript_eslint_1.default.config({
27
27
  },
28
28
  },
29
29
  }, {
30
- files: ['**/*.ts', '**/*.tsx', , '**/*.cts', '**/*.mts'],
30
+ files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts'],
31
31
  rules: {
32
32
  '@typescript-eslint/explicit-member-accessibility': 'off',
33
33
  '@typescript-eslint/explicit-module-boundary-types': 'off',
@@ -9,6 +9,7 @@ export type Options = [
9
9
  ignoredFiles?: string[];
10
10
  includeTransitiveDependencies?: boolean;
11
11
  useLocalPathsForWorkspaceDependencies?: boolean;
12
+ runtimeHelpers?: string[];
12
13
  }
13
14
  ];
14
15
  export type MessageIds = 'missingDependency' | 'obsoleteDependency' | 'versionMismatch' | 'missingDependencySection';
@@ -30,6 +30,7 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
30
30
  checkVersionMismatches: { type: 'boolean' },
31
31
  includeTransitiveDependencies: { type: 'boolean' },
32
32
  useLocalPathsForWorkspaceDependencies: { type: 'boolean' },
33
+ runtimeHelpers: { type: 'array', items: { type: 'string' } },
33
34
  },
34
35
  additionalProperties: false,
35
36
  },
@@ -51,9 +52,10 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
51
52
  ignoredFiles: [],
52
53
  includeTransitiveDependencies: false,
53
54
  useLocalPathsForWorkspaceDependencies: false,
55
+ runtimeHelpers: [],
54
56
  },
55
57
  ],
56
- create(context, [{ buildTargets, ignoredDependencies, ignoredFiles, checkMissingDependencies, checkObsoleteDependencies, checkVersionMismatches, includeTransitiveDependencies, useLocalPathsForWorkspaceDependencies, },]) {
58
+ create(context, [{ buildTargets, ignoredDependencies, ignoredFiles, checkMissingDependencies, checkObsoleteDependencies, checkVersionMismatches, includeTransitiveDependencies, useLocalPathsForWorkspaceDependencies, runtimeHelpers, },]) {
57
59
  if (!(0, runtime_lint_utils_1.getParserServices)(context).isJSON) {
58
60
  return {};
59
61
  }
@@ -83,6 +85,7 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
83
85
  includeTransitiveDependencies,
84
86
  ignoredFiles,
85
87
  useLocalPathsForWorkspaceDependencies,
88
+ runtimeHelpers,
86
89
  });
87
90
  const expectedDependencyNames = Object.keys(npmDependencies);
88
91
  const packageJson = JSON.parse(context.sourceCode.getText());
@@ -129,7 +132,13 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
129
132
  packageRange.startsWith('file:') ||
130
133
  npmDependencies[packageName] === '*' ||
131
134
  packageRange === '*' ||
132
- packageRange === 'workspace:*' ||
135
+ packageRange.startsWith('workspace:') ||
136
+ /**
137
+ * Catalogs can be named, or left unnamed
138
+ * So just checking up until the : will catch both cases
139
+ * e.g. catalog:some-catalog or catalog:
140
+ */
141
+ packageRange.startsWith('catalog:') ||
133
142
  (0, semver_1.satisfies)(npmDependencies[packageName], packageRange, {
134
143
  includePrerelease: true,
135
144
  })) {
@@ -10,6 +10,7 @@ const ast_utils_1 = require("../utils/ast-utils");
10
10
  const graph_utils_1 = require("../utils/graph-utils");
11
11
  const project_graph_utils_1 = require("../utils/project-graph-utils");
12
12
  const runtime_lint_utils_1 = require("../utils/runtime-lint-utils");
13
+ const project_graph_1 = require("nx/src/config/project-graph");
13
14
  exports.RULE_NAME = 'enforce-module-boundaries';
14
15
  exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl/nx/blob/${devkit_1.NX_VERSION}/docs/generated/packages/eslint-plugin/documents/enforce-module-boundaries.md`)({
15
16
  name: exports.RULE_NAME,
@@ -250,7 +251,7 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
250
251
  !(0, graph_utils_1.circularPathHasPair)([sourceProject, targetProject], expandedIgnoreCircularDependencies)) {
251
252
  if (!allowCircularSelfDependency &&
252
253
  !(0, fileutils_1.isRelativePath)(imp) &&
253
- !(0, runtime_lint_utils_1.belongsToDifferentNgEntryPoint)(imp, sourceFilePath, sourceProject.data.root)) {
254
+ !(0, runtime_lint_utils_1.belongsToDifferentEntryPoint)(imp, sourceFilePath, sourceProject.data.root)) {
254
255
  context.report({
255
256
  node,
256
257
  messageId: 'noSelfCircularDependencies',
@@ -328,6 +329,10 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
328
329
  }
329
330
  return;
330
331
  }
332
+ if (!(0, project_graph_1.isProjectGraphProjectNode)(targetProject)) {
333
+ return;
334
+ }
335
+ targetProject = targetProject;
331
336
  // check constraints between libs and apps
332
337
  // check for circular dependency
333
338
  const circularPath = (0, graph_utils_1.checkCircularPath)(projectGraph, sourceProject, targetProject);
@@ -386,7 +391,7 @@ exports.default = utils_1.ESLintUtils.RuleCreator(() => `https://github.com/nrwl
386
391
  }
387
392
  // if we import a library using loadChildren, we should not import it using es6imports
388
393
  if (!checkDynamicDependenciesExceptions.some((a) => (0, runtime_lint_utils_1.matchImportWithWildcard)(a, imp)) &&
389
- (0, runtime_lint_utils_1.hasStaticImportOfDynamicResource)(node, projectGraph, sourceProject.name, targetProject.name)) {
394
+ (0, runtime_lint_utils_1.hasStaticImportOfDynamicResource)(node, projectGraph, sourceProject.name, targetProject.name, imp, sourceFilePath)) {
390
395
  const filesWithLazyImports = (0, graph_utils_1.findFilesWithDynamicImports)(projectFileMap, sourceProject.name, targetProject.name);
391
396
  context.report({
392
397
  data: {
@@ -40,7 +40,7 @@ export declare function isAbsoluteImportIntoAnotherProject(imp: string, workspac
40
40
  }): boolean;
41
41
  export declare function findProjectUsingImport(projectGraph: ProjectGraph, targetProjectLocator: TargetProjectLocator, filePath: string, imp: string): ProjectGraphProjectNode | ProjectGraphExternalNode;
42
42
  export declare function findConstraintsFor(depConstraints: DepConstraint[], sourceProject: ProjectGraphProjectNode): DepConstraint[];
43
- export declare function hasStaticImportOfDynamicResource(node: TSESTree.ImportDeclaration | TSESTree.ImportExpression | TSESTree.ExportAllDeclaration | TSESTree.ExportNamedDeclaration, graph: ProjectGraph, sourceProjectName: string, targetProjectName: string): boolean;
43
+ export declare function hasStaticImportOfDynamicResource(node: TSESTree.ImportDeclaration | TSESTree.ImportExpression | TSESTree.ExportAllDeclaration | TSESTree.ExportNamedDeclaration, graph: ProjectGraph, sourceProjectName: string, targetProjectName: string, importExpr: string, filePath: string): boolean;
44
44
  export declare function getSourceFilePath(sourceFileName: string, projectPath: string): string;
45
45
  export declare function hasBannedImport(source: ProjectGraphProjectNode, target: ProjectGraphExternalNode, depConstraints: DepConstraint[], imp: string): DepConstraint | undefined;
46
46
  /**
@@ -80,7 +80,15 @@ export declare function groupImports(importsToRemap: {
80
80
  /**
81
81
  * Checks if source file belongs to a secondary entry point different than the import one
82
82
  */
83
- export declare function belongsToDifferentNgEntryPoint(importExpr: string, filePath: string, projectRoot: string): boolean;
83
+ export declare function belongsToDifferentEntryPoint(importExpr: string, filePath: string, projectRoot: string): boolean;
84
+ export declare function getSecondaryEntryPointPath(importExpr: string, filePath: string, projectRoot: string): string | undefined;
85
+ export declare function parseExports(exports: string | null | Record<string, any>, projectRoot: string, entryPaths: Array<{
86
+ path: string;
87
+ file: string;
88
+ }>, basePath?: string): Array<{
89
+ path: string;
90
+ file: string;
91
+ }>;
84
92
  /**
85
93
  * Returns true if the given project contains MFE config with "exposes:" section
86
94
  */
@@ -20,7 +20,9 @@ exports.isDirectDependency = isDirectDependency;
20
20
  exports.hasBuildExecutor = hasBuildExecutor;
21
21
  exports.isTerminalRun = isTerminalRun;
22
22
  exports.groupImports = groupImports;
23
- exports.belongsToDifferentNgEntryPoint = belongsToDifferentNgEntryPoint;
23
+ exports.belongsToDifferentEntryPoint = belongsToDifferentEntryPoint;
24
+ exports.getSecondaryEntryPointPath = getSecondaryEntryPointPath;
25
+ exports.parseExports = parseExports;
24
26
  exports.appIsMFERemote = appIsMFERemote;
25
27
  exports.getParserServices = getParserServices;
26
28
  const tslib_1 = require("tslib");
@@ -135,14 +137,15 @@ function findConstraintsFor(depConstraints, sourceProject) {
135
137
  }
136
138
  });
137
139
  }
138
- function hasStaticImportOfDynamicResource(node, graph, sourceProjectName, targetProjectName) {
140
+ function hasStaticImportOfDynamicResource(node, graph, sourceProjectName, targetProjectName, importExpr, filePath) {
139
141
  if (node.type !== utils_1.AST_NODE_TYPES.ImportDeclaration ||
140
142
  node.importKind === 'type') {
141
143
  return false;
142
144
  }
143
- return onlyLoadChildren(graph, sourceProjectName, targetProjectName, []);
145
+ return (hasDynamicImport(graph, sourceProjectName, targetProjectName, []) &&
146
+ !getSecondaryEntryPointPath(importExpr, filePath, graph.nodes[targetProjectName].data.root));
144
147
  }
145
- function onlyLoadChildren(graph, sourceProjectName, targetProjectName, visited) {
148
+ function hasDynamicImport(graph, sourceProjectName, targetProjectName, visited) {
146
149
  if (visited.indexOf(sourceProjectName) > -1) {
147
150
  return false;
148
151
  }
@@ -153,7 +156,7 @@ function onlyLoadChildren(graph, sourceProjectName, targetProjectName, visited)
153
156
  if (d.target === targetProjectName) {
154
157
  return true;
155
158
  }
156
- return onlyLoadChildren(graph, d.target, targetProjectName, [
159
+ return hasDynamicImport(graph, d.target, targetProjectName, [
157
160
  ...visited,
158
161
  sourceProjectName,
159
162
  ]);
@@ -325,21 +328,34 @@ function groupImports(importsToRemap) {
325
328
  /**
326
329
  * Checks if source file belongs to a secondary entry point different than the import one
327
330
  */
328
- function belongsToDifferentNgEntryPoint(importExpr, filePath, projectRoot) {
331
+ function belongsToDifferentEntryPoint(importExpr, filePath, projectRoot) {
332
+ const importEntryPoint = getSecondaryEntryPointPath(importExpr, filePath, projectRoot);
333
+ const srcEntryPoint = getEntryPoint(filePath, projectRoot);
334
+ // check if the entry point of import expression is different than the source file's entry point
335
+ return importEntryPoint !== srcEntryPoint;
336
+ }
337
+ function getSecondaryEntryPointPath(importExpr, filePath, projectRoot) {
329
338
  const resolvedImportFile = (0, internal_1.resolveModuleByImport)(importExpr, filePath, // not strictly necessary, but speeds up resolution
330
339
  path.join(devkit_1.workspaceRoot, (0, js_1.getRootTsConfigFileName)()));
331
340
  if (!resolvedImportFile) {
332
- return false;
341
+ return undefined;
333
342
  }
334
- const importEntryPoint = getAngularEntryPoint(resolvedImportFile, projectRoot);
335
- const srcEntryPoint = getAngularEntryPoint(filePath, projectRoot);
336
- // check if the entry point of import expression is different than the source file's entry point
337
- return importEntryPoint !== srcEntryPoint;
343
+ const entryPoint = getEntryPoint(resolvedImportFile, projectRoot);
344
+ return entryPoint;
338
345
  }
339
- function getAngularEntryPoint(file, projectRoot) {
346
+ function getEntryPoint(file, projectRoot) {
347
+ const packageEntryPoints = getPackageEntryPoints(projectRoot);
348
+ const fileEntryPoint = packageEntryPoints.find((entry) => entry.file === file);
349
+ if (fileEntryPoint) {
350
+ return fileEntryPoint.file;
351
+ }
340
352
  let parent = (0, devkit_1.joinPathFragments)(file, '../');
341
353
  while (parent !== `${projectRoot}/`) {
342
- // we need to find closest existing ng-package.json
354
+ const entryPoint = packageEntryPoints.find((entry) => entry.path === parent);
355
+ if (entryPoint) {
356
+ return entryPoint.file;
357
+ }
358
+ // for Angular we need to find closest existing ng-package.json
343
359
  // in order to determine if the file matches the secondary entry point
344
360
  const ngPackageContent = (0, fileutils_1.readFileIfExisting)(path.join(devkit_1.workspaceRoot, parent, 'ng-package.json'));
345
361
  if (ngPackageContent) {
@@ -351,6 +367,45 @@ function getAngularEntryPoint(file, projectRoot) {
351
367
  }
352
368
  return undefined;
353
369
  }
370
+ function getPackageEntryPoints(projectRoot) {
371
+ const packageContent = (0, fileutils_1.readFileIfExisting)(path.join(devkit_1.workspaceRoot, projectRoot, 'package.json'));
372
+ if (!packageContent) {
373
+ return [];
374
+ }
375
+ const exports = (0, devkit_1.parseJson)(packageContent).exports;
376
+ if (!exports) {
377
+ return [];
378
+ }
379
+ const entryPaths = [];
380
+ parseExports(exports, projectRoot, entryPaths);
381
+ return entryPaths;
382
+ }
383
+ function parseExports(exports, projectRoot, entryPaths, basePath = '.') {
384
+ if (exports === null) {
385
+ return;
386
+ }
387
+ if (typeof exports === 'string') {
388
+ if (basePath === '.') {
389
+ return;
390
+ }
391
+ else {
392
+ entryPaths.push({
393
+ path: (0, devkit_1.joinPathFragments)(projectRoot, basePath),
394
+ file: (0, devkit_1.joinPathFragments)(projectRoot, exports),
395
+ });
396
+ return;
397
+ }
398
+ }
399
+ // parse conditional exports
400
+ if (exports.import || exports.require || exports.default || exports.node) {
401
+ parseExports(exports.default || exports.import || exports.require || exports.node, projectRoot, entryPaths, basePath);
402
+ return;
403
+ }
404
+ // parse general nested exports
405
+ for (const [key, value] of Object.entries(exports)) {
406
+ parseExports(value, projectRoot, entryPaths, key);
407
+ }
408
+ }
354
409
  /**
355
410
  * Returns true if the given project contains MFE config with "exposes:" section
356
411
  */
@@ -1,2 +0,0 @@
1
- import { Tree } from '@nx/devkit';
2
- export default function renameWorkspaceRule(tree: Tree): Promise<void>;
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = renameWorkspaceRule;
4
- const devkit_1 = require("@nx/devkit");
5
- const binary_extensions_1 = require("@nx/devkit/src/utils/binary-extensions");
6
- const constants_1 = require("../../constants");
7
- async function renameWorkspaceRule(tree) {
8
- if (!tree.exists(constants_1.WORKSPACE_RULES_PATH)) {
9
- return;
10
- }
11
- let ruleNames = [];
12
- try {
13
- ruleNames = Object.keys(require(constants_1.WORKSPACE_PLUGIN_DIR).rules);
14
- }
15
- catch (e) {
16
- return;
17
- }
18
- (0, devkit_1.visitNotIgnoredFiles)(tree, '.', (path) => {
19
- if ((0, binary_extensions_1.isBinaryPath)(path)) {
20
- return;
21
- }
22
- let contents = tree.read(path, 'utf-8');
23
- ruleNames.forEach((ruleName) => {
24
- contents = contents.replace(new RegExp(`@nx/workspace/${ruleName}`, 'g'), `@nx/workspace-${ruleName}`);
25
- });
26
- tree.write(path, contents);
27
- });
28
- await (0, devkit_1.formatFiles)(tree);
29
- }