From e328bfa90a4cb4b7fec757db22a7dd6c4e41cf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Thu, 4 May 2023 13:13:38 +0200 Subject: [PATCH] feat(linter): support custom build targets for buildability check (#16743) --- .../rules/enforce-module-boundaries.spec.ts | 57 ++++++++++++++++++- .../src/rules/enforce-module-boundaries.ts | 9 ++- .../src/utils/runtime-lint-utils.ts | 14 +++-- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/src/rules/enforce-module-boundaries.spec.ts b/packages/eslint-plugin/src/rules/enforce-module-boundaries.spec.ts index 410262a103bb5..a4be7307c30ca 100644 --- a/packages/eslint-plugin/src/rules/enforce-module-boundaries.spec.ts +++ b/packages/eslint-plugin/src/rules/enforce-module-boundaries.spec.ts @@ -52,6 +52,7 @@ const tsconfig = { '@mycompany/domain1': ['libs/domain1/src/index.ts'], '@mycompany/domain2': ['libs/domain2/src/index.ts'], '@mycompany/buildableLib': ['libs/buildableLib/src/main.ts'], + '@mycompany/buildableLib2': ['libs/buildableLib2/src/main.ts'], '@nonBuildableScope/nonBuildableLib': [ 'libs/nonBuildableLib/src/main.ts', ], @@ -91,6 +92,7 @@ const fileSys = { './libs/domain1/src/index.ts': '', './libs/domain2/src/index.ts': '', './libs/buildableLib/src/main.ts': '', + './libs/buildableLib2/src/main.ts': '', './libs/nonBuildableLib/src/main.ts': '', './libs/public/src/index.ts': '', './libs/dependsOnPrivate/src/index.ts': '', @@ -1726,10 +1728,63 @@ Circular file chain: expect(failures[1].message).toEqual(message); }); + it('should error when buildable libraries with custom target import non-buildable libraries', () => { + const failures = runRule( + { + enforceBuildableLibDependency: true, + buildTargets: ['my-build'], + }, + `${process.cwd()}/proj/libs/buildableLib2/src/main.ts`, + ` + import '@nonBuildableScope/nonBuildableLib'; + import('@nonBuildableScope/nonBuildableLib'); + `, + { + nodes: { + buildableLib2: { + name: 'buildableLib2', + type: 'lib', + data: { + root: 'libs/buildableLib2', + tags: [], + implicitDependencies: [], + targets: { + 'my-build': { + // defines a buildable lib + executor: '@angular-devkit/build-ng-packagr:build', + }, + }, + files: [createFile(`libs/buildableLib2/src/main.ts`)], + }, + }, + nonBuildableLib: { + name: 'nonBuildableLib', + type: 'lib', + data: { + root: 'libs/nonBuildableLib', + tags: [], + implicitDependencies: [], + targets: {}, + files: [createFile(`libs/nonBuildableLib/src/main.ts`)], + }, + }, + }, + dependencies: {}, + } + ); + + const message = + 'Buildable libraries cannot import or export from non-buildable libraries'; + expect(failures.length).toEqual(2); + expect(failures[0].message).toEqual(message); + expect(failures[1].message).toEqual(message); + }); + it('should not error when buildable libraries import another buildable libraries', () => { const failures = runRule( { enforceBuildableLibDependency: true, + buildTargets: ['my-build', 'build'], }, `${process.cwd()}/proj/libs/buildableLib/src/main.ts`, ` @@ -1762,7 +1817,7 @@ Circular file chain: tags: [], implicitDependencies: [], targets: { - build: { + 'my-build': { // defines a buildable lib executor: '@angular-devkit/build-ng-packagr:build', }, diff --git a/packages/eslint-plugin/src/rules/enforce-module-boundaries.ts b/packages/eslint-plugin/src/rules/enforce-module-boundaries.ts index 75d371936d746..7f82e27f4c0ad 100644 --- a/packages/eslint-plugin/src/rules/enforce-module-boundaries.ts +++ b/packages/eslint-plugin/src/rules/enforce-module-boundaries.ts @@ -33,7 +33,6 @@ import { isComboDepConstraint, } from '../utils/runtime-lint-utils'; import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; -import { TargetProjectLocator } from '@nx/js/src/internal'; import { basename, dirname, relative } from 'path'; import { getBarrelEntryPointByImportScope, @@ -46,6 +45,7 @@ import { readProjectGraph } from '../utils/project-graph-utils'; type Options = [ { allow: string[]; + buildTargets: string[]; depConstraints: DepConstraint[]; enforceBuildableLibDependency: boolean; allowCircularSelfDependency: boolean; @@ -90,6 +90,7 @@ export default createESLintRule({ banTransitiveDependencies: { type: 'boolean' }, checkNestedExternalImports: { type: 'boolean' }, allow: [{ type: 'string' }], + buildTargets: [{ type: 'string' }], depConstraints: [ { type: 'object', @@ -138,6 +139,7 @@ export default createESLintRule({ defaultOptions: [ { allow: [], + buildTargets: ['build'], depConstraints: [], enforceBuildableLibDependency: false, allowCircularSelfDependency: false, @@ -151,6 +153,7 @@ export default createESLintRule({ [ { allow, + buildTargets, depConstraints, enforceBuildableLibDependency, allowCircularSelfDependency, @@ -479,8 +482,8 @@ export default createESLintRule({ targetProject.type === 'lib' ) { if ( - hasBuildExecutor(sourceProject) && - !hasBuildExecutor(targetProject) + hasBuildExecutor(sourceProject, buildTargets) && + !hasBuildExecutor(targetProject, buildTargets) ) { context.report({ node, diff --git a/packages/eslint-plugin/src/utils/runtime-lint-utils.ts b/packages/eslint-plugin/src/utils/runtime-lint-utils.ts index e34403d7f7825..dcd6c9a54feea 100644 --- a/packages/eslint-plugin/src/utils/runtime-lint-utils.ts +++ b/packages/eslint-plugin/src/utils/runtime-lint-utils.ts @@ -381,17 +381,21 @@ function parseImportWildcards(importDefinition: string): RegExp { } /** - * Verifies whether the given node has an architect builder attached + * Verifies whether the given node has a builder target * @param projectGraph the node to verify + * @param buildTargets the list of targets to check for */ export function hasBuildExecutor( - projectGraph: ProjectGraphProjectNode + projectGraph: ProjectGraphProjectNode, + buildTargets = ['build'] ): boolean { return ( - // can the architect not be defined? real use case? projectGraph.data.targets && - projectGraph.data.targets.build && - projectGraph.data.targets.build.executor !== '' + buildTargets.some( + (target) => + projectGraph.data.targets[target] && + projectGraph.data.targets[target].executor !== '' + ) ); }