fix(linter): dependency-check-support-catalogs (#29923)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Currently there doesn't seem to be support for pnpm catalogs in the
dependency check which throws an error.



## Expected Behavior
Catalog should be supported and at least not throw an error since its a
default rule in some generators.
I noticed that also that only `workspace:*` was implemented so I added
the other possible options.

https://pnpm.io/workspaces#publishing-workspace-packages

I copy and pasted a test twice to add tests for this.

I mean ideally I guess we would check the `pnpm-workspace` file and say
is this catalog defined, I don't think that's up my ally for this
purpose and I'm not sure if how its implemented does that for the
workspace protocols as well.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
closed #29903, #29959


Gave it a shot, not sure if this is gonna be comprehensive enough. But
let me know! happy to try and get this right.

---------

Co-authored-by: Jack Hsu <jack.hsu@gmail.com>
This commit is contained in:
Ryan Bas 2025-02-11 13:20:51 -07:00 committed by GitHub
parent 3815df40e1
commit 2d9a673ec8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 136 additions and 3 deletions

View File

@ -30,6 +30,8 @@ const rootPackageJson = {
dependencies: { dependencies: {
external1: '~16.1.2', external1: '~16.1.2',
external2: '^5.2.0', external2: '^5.2.0',
external3: '1.0.0',
external4: '1.0.0',
}, },
devDependencies: { devDependencies: {
tslib: '^2.1.0', tslib: '^2.1.0',
@ -53,6 +55,22 @@ const externalNodes: Record<string, ProjectGraphExternalNode> = {
version: '5.5.6', version: '5.5.6',
}, },
}, },
'npm:external3': {
name: 'npm:external3',
type: 'npm',
data: {
packageName: 'external3',
version: '1.0.0',
},
},
'npm:external4': {
name: 'npm:external3',
type: 'npm',
data: {
packageName: 'external4',
version: '1.0.0',
},
},
'npm:random-external': { 'npm:random-external': {
name: 'npm:random-external', name: 'npm:random-external',
type: 'npm', type: 'npm',
@ -1581,12 +1599,121 @@ describe('Dependency checks (eslint)', () => {
`); `);
}); });
it('should not report * and workspace:*', () => { it('should not report *', () => {
const packageJson = { const packageJson = {
name: '@mycompany/liba', name: '@mycompany/liba',
dependencies: { dependencies: {
external1: '*', external1: '*',
external2: 'workspace:*', },
};
const fileSys = {
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
'./libs/liba/src/index.ts': '',
'./package.json': JSON.stringify(rootPackageJson, null, 2),
};
vol.fromJSON(fileSys, '/root');
const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: {
liba: {
name: 'liba',
type: 'lib',
data: {
root: 'libs/liba',
targets: {
build: {},
},
},
},
},
externalNodes,
dependencies: {
liba: [{ source: 'liba', target: 'npm:external1', type: 'static' }],
},
},
{
liba: [createFile(`libs/liba/src/main.ts`, ['npm:external1'])],
}
);
expect(failures.length).toEqual(0);
});
it('should not report workspace: protocol', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
external1: 'workspace:~',
external2: 'workspace:^',
external3: 'workspace:',
external4: 'workspace:../external4',
},
};
const fileSys = {
'./libs/liba/package.json': JSON.stringify(packageJson, null, 2),
'./libs/liba/src/index.ts': '',
'./package.json': JSON.stringify(rootPackageJson, null, 2),
};
vol.fromJSON(fileSys, '/root');
const failures = runRule(
{},
`/root/libs/liba/package.json`,
JSON.stringify(packageJson, null, 2),
{
nodes: {
liba: {
name: 'liba',
type: 'lib',
data: {
root: 'libs/liba',
targets: {
build: {},
},
},
},
},
externalNodes,
dependencies: {
liba: [
{ source: 'liba', target: 'npm:external1', type: 'static' },
{ source: 'liba', target: 'npm:external2', type: 'static' },
{ source: 'liba', target: 'npm:external3', type: 'static' },
{ source: 'liba', target: 'npm:external4', type: 'static' },
],
},
},
{
liba: [
createFile(`libs/liba/src/main.ts`, [
'npm:external1',
'npm:external2',
'npm:external3',
'npm:external4',
]),
createFile(`libs/liba/package.json`, [
'npm:external1',
'npm:external2',
'npm:external3',
'npm:external4',
]),
],
}
);
expect(failures.length).toEqual(0);
});
it('should not report catalog: protocol', () => {
const packageJson = {
name: '@mycompany/liba',
dependencies: {
external1: 'catalog:',
external2: 'catalog:nx',
}, },
}; };

View File

@ -218,7 +218,13 @@ export default ESLintUtils.RuleCreator(
packageRange.startsWith('file:') || packageRange.startsWith('file:') ||
npmDependencies[packageName] === '*' || npmDependencies[packageName] === '*' ||
packageRange === '*' || packageRange === '*' ||
packageRange === 'workspace:*' || packageRange.startsWith('workspace:') ||
/**
* Catalogs can be named, or left unnamed
* So just checking up until the : will catch both cases
* e.g. catalog:some-catalog or catalog:
*/
packageRange.startsWith('catalog:') ||
satisfies(npmDependencies[packageName], packageRange, { satisfies(npmDependencies[packageName], packageRange, {
includePrerelease: true, includePrerelease: true,
}) })