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
|
```jsonc
|
||||||
{
|
{
|
||||||
@ -339,10 +339,10 @@ Another common example is ensuring that util libraries stay framework-free by ba
|
|||||||
"allow": [],
|
"allow": [],
|
||||||
// update depConstraints based on your tags
|
// update depConstraints based on your tags
|
||||||
"depConstraints": [
|
"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",
|
"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
|
```jsonc
|
||||||
{
|
{
|
||||||
@ -339,10 +339,10 @@ Another common example is ensuring that util libraries stay framework-free by ba
|
|||||||
"allow": [],
|
"allow": [],
|
||||||
// update depConstraints based on your tags
|
// update depConstraints based on your tags
|
||||||
"depConstraints": [
|
"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",
|
"sourceTag": "type:ui",
|
||||||
"bannedExternalImports": ["react", "react-dom"]
|
"bannedExternalImports": ["*react*"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -260,6 +260,30 @@ describe('Enforce Module Boundaries (eslint)', () => {
|
|||||||
version: '0.0.0',
|
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: {},
|
dependencies: {},
|
||||||
};
|
};
|
||||||
@ -331,6 +355,30 @@ describe('Enforce Module Boundaries (eslint)', () => {
|
|||||||
expect(failures[1].message).toEqual(message);
|
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', () => {
|
it('should error when the target library is untagged', () => {
|
||||||
const failures = runRule(
|
const failures = runRule(
|
||||||
depConstraints,
|
depConstraints,
|
||||||
|
|||||||
@ -187,10 +187,22 @@ export function hasBannedImport(
|
|||||||
c.bannedExternalImports.length
|
c.bannedExternalImports.length
|
||||||
);
|
);
|
||||||
return depConstraints.find((constraint) =>
|
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
|
* Verifies whether the given node has an architect builder attached
|
||||||
* @param projectGraph the node to verify
|
* @param projectGraph the node to verify
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user