feat(linter): allow wildcards for banning external imports (#7623)

This commit is contained in:
Miroslav Jonaš 2021-11-05 09:25:05 -05:00 committed by GitHub
parent 878b44d997
commit f23a4cf7f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 7 deletions

View File

@ -327,7 +327,7 @@ A common example of this is for backend projects that use NestJS and frontend pr
}
```
Another common example is ensuring that util libraries stay framework-free by banning imports from these frameworks. A workspace using React would have a configuration like this.
Another common example is ensuring that util libraries stay framework-free by banning imports from these frameworks. You can use wildcard `*` to match multiple projects e.g. `react*` would match `react`, but also `react-dom`, `react-native` etc. You can also have multiple wildcards e.g. `*react*` would match any package with word `react` in it's name. A workspace using React would have a configuration like this.
```jsonc
{
@ -339,10 +339,10 @@ Another common example is ensuring that util libraries stay framework-free by ba
"allow": [],
// update depConstraints based on your tags
"depConstraints": [
// projects tagged with "type:ui" can't import from "react" or "react-dom"
// projects tagged with "type:ui" can't import from "react" or related projects
{
"sourceTag": "type:ui",
"bannedExternalImports": ["react", "react-dom"]
"bannedExternalImports": ["*react*"]
}
]
}

View File

@ -327,7 +327,7 @@ A common example of this is for backend projects that use NestJS and frontend pr
}
```
Another common example is ensuring that util libraries stay framework-free by banning imports from these frameworks. A workspace using React would have a configuration like this.
Another common example is ensuring that util libraries stay framework-free by banning imports from these frameworks. You can use wildcard `*` to match multiple projects e.g. `react*` would match `react`, but also `react-dom`, `react-native` etc. You can also have multiple wildcards e.g. `*react*` would match any package with word `react` in it's name. A workspace using React would have a configuration like this.
```jsonc
{
@ -339,10 +339,10 @@ Another common example is ensuring that util libraries stay framework-free by ba
"allow": [],
// update depConstraints based on your tags
"depConstraints": [
// projects tagged with "type:ui" can't import from "react" or "react-dom"
// projects tagged with "type:ui" can't import from "react" or related projects
{
"sourceTag": "type:ui",
"bannedExternalImports": ["react", "react-dom"]
"bannedExternalImports": ["*react*"]
}
]
}

View File

@ -260,6 +260,30 @@ describe('Enforce Module Boundaries (eslint)', () => {
version: '0.0.0',
},
},
'npm:npm-package2': {
name: 'npm:npm-package2',
type: 'npm',
data: {
packageName: 'npm-package2',
version: '0.0.0',
},
},
'npm:1npm-package': {
name: 'npm:1npm-package',
type: 'npm',
data: {
packageName: '1npm-package',
version: '0.0.0',
},
},
'npm:npm-awesome-package': {
name: 'npm:npm-awesome-package',
type: 'npm',
data: {
packageName: 'npm-awesome-package',
version: '0.0.0',
},
},
},
dependencies: {},
};
@ -331,6 +355,30 @@ describe('Enforce Module Boundaries (eslint)', () => {
expect(failures[1].message).toEqual(message);
});
it('should allow wildcards for defining forbidden npm packages', () => {
const failures = runRule(
{
depConstraints: [
{ sourceTag: 'api', bannedExternalImports: ['npm-*ge'] },
],
},
`${process.cwd()}/proj/libs/api/src/index.ts`,
`
import 'npm-package';
import 'npm-awesome-package';
import 'npm-package2';
import '1npm-package';
`,
graph
);
const message = (packageName) =>
`A project tagged with "api" is not allowed to import the "${packageName}" package`;
expect(failures.length).toEqual(2);
expect(failures[0].message).toEqual(message('npm-package'));
expect(failures[1].message).toEqual(message('npm-awesome-package'));
});
it('should error when the target library is untagged', () => {
const failures = runRule(
depConstraints,

View File

@ -187,10 +187,22 @@ export function hasBannedImport(
c.bannedExternalImports.length
);
return depConstraints.find((constraint) =>
constraint.bannedExternalImports.includes(target.data.packageName)
constraint.bannedExternalImports.some((importDefinition) =>
parseImportWildcards(importDefinition).test(target.data.packageName)
)
);
}
/**
* Maps import with wildcards to regex pattern
* @param importDefinition
* @returns
*/
function parseImportWildcards(importDefinition: string): RegExp {
const mappedWildcards = importDefinition.split('*').join('.*');
return new RegExp(`^${new RegExp(mappedWildcards).source}$`);
}
/**
* Verifies whether the given node has an architect builder attached
* @param projectGraph the node to verify