@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 +0 -6
- package/package.json +4 -4
- package/src/flat-configs/typescript.js +1 -1
- package/src/rules/dependency-checks.d.ts +1 -0
- package/src/rules/dependency-checks.js +11 -2
- package/src/rules/enforce-module-boundaries.js +7 -2
- package/src/utils/runtime-lint-utils.d.ts +10 -2
- package/src/utils/runtime-lint-utils.js +68 -13
- package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.d.ts +0 -2
- package/src/migrations/update-17-2-6-rename-workspace-rules/rename-workspace-rules.js +0 -29
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.
|
|
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": "^
|
|
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.
|
|
38
|
-
"@nx/js": "21.0.0-beta.
|
|
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',
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
145
|
+
return (hasDynamicImport(graph, sourceProjectName, targetProjectName, []) &&
|
|
146
|
+
!getSecondaryEntryPointPath(importExpr, filePath, graph.nodes[targetProjectName].data.root));
|
|
144
147
|
}
|
|
145
|
-
function
|
|
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
|
|
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
|
|
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
|
|
341
|
+
return undefined;
|
|
333
342
|
}
|
|
334
|
-
const
|
|
335
|
-
|
|
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
|
|
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
|
-
|
|
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,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
|
-
}
|