Skip to content

Commit

Permalink
feat(core): implement notDependOnLibsWithTags lib dependency constraint
Browse files Browse the repository at this point in the history
ISSUES CLOSED: nrwl#984

(cherry picked from commit 89c69d7)
  • Loading branch information
jaytavares authored and meeroslav committed Mar 4, 2022
1 parent c1ebe80 commit 9843005
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
Expand Up @@ -249,6 +249,28 @@ describe('Enforce Module Boundaries (eslint)', () => {
files: [createFile(`libs/impl/src/index.ts`)],
},
},
publicName: {
name: 'publicName',
type: ProjectType.lib,
data: {
root: 'libs/public',
tags: ['public'],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/public/src/index.ts`)],
},
},
privateName: {
name: 'privateName',
type: ProjectType.lib,
data: {
root: 'libs/private',
tags: ['private'],
implicitDependencies: [],
architect: {},
files: [createFile(`libs/private/src/index.ts`)],
},
},
untaggedName: {
name: 'untaggedName',
type: ProjectType.lib,
Expand Down Expand Up @@ -304,6 +326,7 @@ describe('Enforce Module Boundaries (eslint)', () => {
{ sourceTag: 'impl', onlyDependOnLibsWithTags: ['api', 'impl'] },
{ sourceTag: 'domain1', onlyDependOnLibsWithTags: ['domain1'] },
{ sourceTag: 'domain2', onlyDependOnLibsWithTags: ['domain2'] },
{ sourceTag: 'public', notDependOnLibsWithTags: ['private'] },
],
};

Expand Down Expand Up @@ -447,6 +470,24 @@ describe('Enforce Module Boundaries (eslint)', () => {
expect(failures[1].message).toEqual(message);
});

it('should error when the target library has a disallowed tag', () => {
const failures = runRule(
depConstraints,
`${process.cwd()}/proj/libs/public/src/index.ts`,
`
import '@mycompany/private';
import('@mycompany/private');
`,
graph
);

const message =
'A project tagged with "public" can not depend on libs tagged with "private"';
expect(failures.length).toEqual(2);
expect(failures[0].message).toEqual(message);
expect(failures[1].message).toEqual(message);
});

it('should error when the source library is untagged', () => {
const failures = runRule(
depConstraints,
Expand Down
32 changes: 27 additions & 5 deletions packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.ts
Expand Up @@ -7,6 +7,7 @@ import {
getSourceFilePath,
hasBuildExecutor,
hasNoneOfTheseTags,
hasAnyOfTheseTags,
isAbsoluteImportIntoAnotherProject,
isRelativeImportIntoAnotherProject,
mapProjectGraphFiles,
Expand Down Expand Up @@ -55,9 +56,10 @@ export type MessageIds =
| 'noImportOfNonBuildableLibraries'
| 'noImportsOfLazyLoadedLibraries'
| 'projectWithoutTagsCannotHaveDependencies'
| 'tagConstraintViolation'
| 'bannedExternalImportsViolation'
| 'noTransitiveDependencies';
| 'noTransitiveDependencies'
| 'onlyTagsConstraintViolation'
| 'notTagsConstraintViolation';
export const RULE_NAME = 'enforce-module-boundaries';

export default createESLintRule<Options, MessageIds>({
Expand All @@ -84,6 +86,7 @@ export default createESLintRule<Options, MessageIds>({
sourceTag: { type: 'string' },
onlyDependOnLibsWithTags: [{ type: 'string' }],
bannedExternalImports: [{ type: 'string' }],
notDependOnLibsWithTags: [{ type: 'string' }],
},
additionalProperties: false,
},
Expand All @@ -102,9 +105,10 @@ export default createESLintRule<Options, MessageIds>({
'Buildable libraries cannot import or export from non-buildable libraries',
noImportsOfLazyLoadedLibraries: `Imports of lazy-loaded libraries are forbidden`,
projectWithoutTagsCannotHaveDependencies: `A project without tags matching at least one constraint cannot depend on any libraries`,
tagConstraintViolation: `A project tagged with "{{sourceTag}}" can only depend on libs tagged with {{allowedTags}}`,
bannedExternalImportsViolation: `A project tagged with "{{sourceTag}}" is not allowed to import the "{{package}}" package`,
noTransitiveDependencies: `Transitive dependencies are not allowed. Only packages defined in the "package.json" can be imported`,
onlyTagsConstraintViolation: `A project tagged with "{{sourceTag}}" can only depend on libs tagged with {{allowedTags}}`,
notTagsConstraintViolation: `A project tagged with "{{sourceTag}}" can not depend on libs tagged with {{disallowedTags}}`,
},
},
defaultOptions: [
Expand Down Expand Up @@ -393,24 +397,42 @@ export default createESLintRule<Options, MessageIds>({

for (let constraint of constraints) {
if (
constraint.onlyDependOnLibsWithTags &&
hasNoneOfTheseTags(
targetProject,
constraint.onlyDependOnLibsWithTags || []
constraint.onlyDependOnLibsWithTags
)
) {
const allowedTags = constraint.onlyDependOnLibsWithTags
.map((s) => `"${s}"`)
.join(', ');
context.report({
node,
messageId: 'tagConstraintViolation',
messageId: 'onlyTagsConstraintViolation',
data: {
sourceTag: constraint.sourceTag,
allowedTags,
},
});
return;
}
if (
constraint.notDependOnLibsWithTags &&
hasAnyOfTheseTags(targetProject, constraint.notDependOnLibsWithTags)
) {
const disallowedTags = constraint.notDependOnLibsWithTags
.map((s) => `"${s}"`)
.join(', ');
context.report({
node,
messageId: 'notTagsConstraintViolation',
data: {
sourceTag: constraint.sourceTag,
disallowedTags: disallowedTags,
},
});
return;
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions packages/workspace/src/utils/runtime-lint-utils.ts
Expand Up @@ -26,6 +26,7 @@ export type Deps = { [projectName: string]: ProjectGraphDependency[] };
export type DepConstraint = {
sourceTag: string;
onlyDependOnLibsWithTags: string[];
notDependOnLibsWithTags: string[];
bannedExternalImports?: string[];
};

Expand All @@ -36,6 +37,12 @@ export function hasNoneOfTheseTags(
return tags.filter((tag) => hasTag(proj, tag)).length === 0;
}

export function hasAnyOfTheseTags(proj: ProjectGraphProjectNode, tags: string[]) {
return (
tags.filter((disallowedTag) => hasTag(proj, disallowedTag)).length !== 0
);
}

function hasTag(proj: ProjectGraphProjectNode, tag: string) {
return tag === '*' || (proj.data.tags || []).indexOf(tag) > -1;
}
Expand Down

0 comments on commit 9843005

Please sign in to comment.