diff --git a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts index 29edb79d16023..e482b999c5371 100644 --- a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts +++ b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts @@ -26,6 +26,7 @@ const tsconfig = { paths: { '@mycompany/impl': ['libs/impl/src/index.ts'], '@mycompany/untagged': ['libs/untagged/src/index.ts'], + '@mycompany/tagged': ['libs/tagged/src/index.ts'], '@mycompany/api': ['libs/api/src/index.ts'], '@mycompany/impl-domain2': ['libs/impl-domain2/src/index.ts'], '@mycompany/impl-both-domains': ['libs/impl-both-domains/src/index.ts'], @@ -69,6 +70,7 @@ const packageJson = { const fileSys = { './libs/impl/src/index.ts': '', './libs/untagged/src/index.ts': '', + './libs/tagged/src/index.ts': '', './libs/api/src/index.ts': '', './libs/impl-domain2/src/index.ts': '', './libs/impl-both-domains/src/index.ts': '', @@ -310,9 +312,10 @@ describe('Enforce Module Boundaries (eslint)', () => { implicitDependencies: [], architect: {}, files: [ - createFile( - `libs/private/src/index.tslibs/private/src/index.tslibs/private/src/index.ts` - ), + createFile(`libs/private/src/index.ts`, [ + 'untaggedName', + 'taggedName', + ]), ], }, }, @@ -327,6 +330,17 @@ describe('Enforce Module Boundaries (eslint)', () => { files: [createFile(`libs/untagged/src/index.ts`)], }, }, + taggedName: { + name: 'taggedName', + type: 'lib', + data: { + root: 'libs/tagged', + tags: ['some-tag'], + implicitDependencies: [], + architect: {}, + files: [createFile(`libs/tagged/src/index.ts`)], + }, + }, }, externalNodes: { 'npm:npm-package': { @@ -387,6 +401,7 @@ describe('Enforce Module Boundaries (eslint)', () => { { sourceTag: 'domain1', onlyDependOnLibsWithTags: ['domain1'] }, { sourceTag: 'domain2', onlyDependOnLibsWithTags: ['domain2'] }, { sourceTag: 'public', notDependOnLibsWithTags: ['private'] }, + { sourceTag: 'private', onlyDependOnLibsWithTags: [] }, ], }; @@ -530,6 +545,38 @@ describe('Enforce Module Boundaries (eslint)', () => { expect(failures[1].message).toEqual(message); }); + it('should not error when the target library is untagged, if source expects it', () => { + const failures = runRule( + depConstraints, + `${process.cwd()}/proj/libs/private/src/index.ts`, + ` + import '@mycompany/untagged'; + import('@mycompany/untagged'); + `, + graph + ); + + expect(failures.length).toEqual(0); + }); + + it('should error when the target library is tagged, if source does not expect it', () => { + const failures = runRule( + depConstraints, + `${process.cwd()}/proj/libs/private/src/index.ts`, + ` + import '@mycompany/tagged'; + import('@mycompany/tagged'); + `, + graph + ); + + const message = + 'A project tagged with "private" cannot depend on any libs with tags'; + expect(failures.length).toEqual(2); + expect(failures[0].message).toEqual(message); + expect(failures[1].message).toEqual(message); + }); + it('should error when the target library has a disallowed tag', () => { const failures = runRule( depConstraints, diff --git a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.ts b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.ts index 68fd6e6edaeab..ea637fef0fa94 100644 --- a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.ts +++ b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.ts @@ -66,6 +66,7 @@ export type MessageIds = | 'nestedBannedExternalImportsViolation' | 'noTransitiveDependencies' | 'onlyTagsConstraintViolation' + | 'emptyOnlyTagsConstraintViolation' | 'notTagsConstraintViolation'; export const RULE_NAME = 'enforce-module-boundaries'; @@ -117,6 +118,8 @@ export default createESLintRule({ nestedBannedExternalImportsViolation: `A project tagged with "{{sourceTag}}" is not allowed to import the "{{package}}" package. Nested import found at {{childProjectName}}`, 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 {{tags}}`, + emptyOnlyTagsConstraintViolation: + 'A project tagged with "{{sourceTag}}" cannot depend on any libs with tags', notTagsConstraintViolation: `A project tagged with "{{sourceTag}}" can not depend on libs tagged with {{tags}}\n\nViolation detected in:\n{{projects}}`, }, }, @@ -516,6 +519,20 @@ export default createESLintRule({ }); return; } + if ( + constraint.onlyDependOnLibsWithTags && + constraint.onlyDependOnLibsWithTags.length === 0 && + targetProject.data.tags.length !== 0 + ) { + context.report({ + node, + messageId: 'emptyOnlyTagsConstraintViolation', + data: { + sourceTag: constraint.sourceTag, + }, + }); + return; + } if ( constraint.notDependOnLibsWithTags && constraint.notDependOnLibsWithTags.length