fix(linter): disable absolute paths within project (#5555)

* fix(linter): disable absolute paths within project

* fix(linter): add missing colon before the import path

* feat(linter): add flag to disable self circular check
This commit is contained in:
Miroslav Jonaš 2021-05-06 13:17:28 +02:00 committed by GitHub
parent e20cf134a8
commit f28097e264
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 152 additions and 8 deletions

View File

@ -19,28 +19,27 @@ import {
import { createESLintRule } from '../utils/create-eslint-rule';
import { normalizePath } from '@nrwl/devkit';
import {
createProjectGraph,
isNpmProject,
ProjectGraph,
ProjectType,
} from '@nrwl/workspace/src/core/project-graph';
import {
readNxJson,
readWorkspaceJson,
} from '@nrwl/workspace/src/core/file-utils';
import { readNxJson } from '@nrwl/workspace/src/core/file-utils';
import { TargetProjectLocator } from '@nrwl/workspace/src/core/target-project-locator';
import { checkCircularPath } from '@nrwl/workspace/src/utils/graph-utils';
import { readCurrentProjectGraph } from '@nrwl/workspace/src/core/project-graph/project-graph';
import { isRelativePath } from '@nrwl/workspace/src/utilities/fileutils';
type Options = [
{
allow: string[];
depConstraints: DepConstraint[];
enforceBuildableLibDependency: boolean;
allowCircularSelfDependency: boolean;
}
];
export type MessageIds =
| 'noRelativeOrAbsoluteImportsAcrossLibraries'
| 'noSelfCircularDependencies'
| 'noCircularDependencies'
| 'noImportsOfApps'
| 'noImportsOfE2e'
@ -83,6 +82,7 @@ export default createESLintRule<Options, MessageIds>({
messages: {
noRelativeOrAbsoluteImportsAcrossLibraries: `Libraries cannot be imported by a relative or absolute path, and must begin with a npm scope`,
noCircularDependencies: `Circular dependency between "{{sourceProjectName}}" and "{{targetProjectName}}" detected: {{path}}`,
noSelfCircularDependencies: `Only relative imports are allowed within the project. Absolute import found: {{imp}}`,
noImportsOfApps: 'Imports of apps are forbidden',
noImportsOfE2e: 'Imports of e2e projects are forbidden',
noImportOfNonBuildableLibraries:
@ -97,9 +97,20 @@ export default createESLintRule<Options, MessageIds>({
allow: [],
depConstraints: [],
enforceBuildableLibDependency: false,
allowCircularSelfDependency: false,
},
],
create(context, [{ allow, depConstraints, enforceBuildableLibDependency }]) {
create(
context,
[
{
allow,
depConstraints,
enforceBuildableLibDependency,
allowCircularSelfDependency,
},
]
) {
/**
* Globally cached info about workspace
*/
@ -193,6 +204,16 @@ export default createESLintRule<Options, MessageIds>({
// same project => allow
if (sourceProject === targetProject) {
// we only allow relative paths within the same project
if (!allowCircularSelfDependency && !isRelativePath(imp)) {
context.report({
node,
messageId: 'noSelfCircularDependencies',
data: {
imp,
},
});
}
return;
}

View File

@ -6,12 +6,11 @@ import {
import { TSESLint } from '@typescript-eslint/experimental-utils';
import * as parser from '@typescript-eslint/parser';
import { vol } from 'memfs';
import { extname, join } from 'path';
import { extname } from 'path';
import enforceModuleBoundaries, {
RULE_NAME as enforceModuleBoundariesRuleName,
} from '../../src/rules/enforce-module-boundaries';
import { TargetProjectLocator } from '@nrwl/workspace/src/core/target-project-locator';
import { readFileSync } from 'fs';
jest.mock('fs', () => require('memfs').fs);
jest.mock('../../../workspace/src/utilities/app-root', () => ({
appRootPath: '/root',
@ -794,6 +793,130 @@ describe('Enforce Module Boundaries', () => {
expect(failures[1].message).toEqual(message);
});
it('should error when absolute path within project detected', () => {
const failures = runRule(
{},
`${process.cwd()}/proj/libs/mylib/src/main.ts`,
`
import '@mycompany/mylib';
import('@mycompany/mylib');
`,
{
nodes: {
mylibName: {
name: 'mylibName',
type: ProjectType.lib,
data: {
root: 'libs/mylib',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/mylib/src/main.ts`)],
},
},
anotherlibName: {
name: 'anotherlibName',
type: ProjectType.lib,
data: {
root: 'libs/anotherlib',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/anotherlib/src/main.ts`)],
},
},
myappName: {
name: 'myappName',
type: ProjectType.app,
data: {
root: 'apps/myapp',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`apps/myapp/src/index.ts`)],
},
},
},
dependencies: {
mylibName: [
{
source: 'mylibName',
target: 'anotherlibName',
type: DependencyType.static,
},
],
},
}
);
const message =
'Only relative imports are allowed within the project. Absolute import found: @mycompany/mylib';
expect(failures.length).toEqual(2);
expect(failures[0].message).toEqual(message);
expect(failures[1].message).toEqual(message);
});
it('should ignore detected absolute path within project if allowCircularSelfDependency flag is set', () => {
const failures = runRule(
{
allowCircularSelfDependency: true,
},
`${process.cwd()}/proj/libs/mylib/src/main.ts`,
`
import '@mycompany/mylib';
import('@mycompany/mylib');
`,
{
nodes: {
mylibName: {
name: 'mylibName',
type: ProjectType.lib,
data: {
root: 'libs/mylib',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/mylib/src/main.ts`)],
},
},
anotherlibName: {
name: 'anotherlibName',
type: ProjectType.lib,
data: {
root: 'libs/anotherlib',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/anotherlib/src/main.ts`)],
},
},
myappName: {
name: 'myappName',
type: ProjectType.app,
data: {
root: 'apps/myapp',
tags: [],
implicitDependencies: [],
architect: {},
files: [createFile(`apps/myapp/src/index.ts`)],
},
},
},
dependencies: {
mylibName: [
{
source: 'mylibName',
target: 'anotherlibName',
type: DependencyType.static,
},
],
},
}
);
expect(failures.length).toBe(0);
});
it('should error when circular dependency detected', () => {
const failures = runRule(
{},