feat(linter): allow wildcards for banning external imports (#7623)
This commit is contained in:
parent
878b44d997
commit
f23a4cf7f1
@ -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*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user