Skip to content

Commit

Permalink
feat: allow direct peer dependencies to be satisfied by another direc…
Browse files Browse the repository at this point in the history
…t peer dependency
  • Loading branch information
christophehurpeau committed Mar 13, 2023
1 parent 616c729 commit 2aba256
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
28 changes: 28 additions & 0 deletions src/checks/checkDirectPeerDependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,32 @@ describe('checkDirectPeerDependencies', () => {
false,
);
});

it('should not report error when peer dependency is provided by another dependency', async () => {
await checkDirectPeerDependencies(
false,
{
name: 'test',
dependencies: {
'some-lib-using-rollup': '1.0.0',
'some-lib-providing-rollup': '1.0.0',
},
},
'path',
jest
.fn()
.mockImplementationOnce(() => ({
name: 'some-lib-providing-rollup',
dependencies: { rollup: '^1.0.0' },
}))
.mockImplementationOnce(() => ({
name: 'some-lib-using-rollup',
peerDependencies: { rollup: '^1.0.0' },
})),
createOnlyWarnsForMappingCheck('test', []),
createOnlyWarnsForMappingCheck('test', []),
createReportError,
);
expect(mockReportError).not.toHaveBeenCalled();
});
});
35 changes: 26 additions & 9 deletions src/checks/checkDirectPeerDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,45 @@ export async function checkDirectPeerDependencies(
): Promise<void> {
const reportError = customCreateReportError('Peer Dependencies', pkgPathName);

const allDepPkgs: {
name: string;
type: RegularDependencyTypes;
pkg: PackageJson;
}[] = [];
const allDirectDependenciesDependencies: [string, string][] = [];

await Promise.all(
regularDependencyTypes.map(async (depType) => {
const dependencies = pkg[depType];
if (!dependencies) return;
for (const depName of getKeys(dependencies)) {
const depPkg = await getDependencyPackageJson(depName);
allDepPkgs.push({ name: depName, type: depType, pkg: depPkg });

if (depPkg.peerDependencies) {
checkPeerDependencies(
pkg,
reportError,
depType,
getAllowedPeerInFromType(depType, isLibrary),
depPkg,
missingOnlyWarnsForCheck.createFor(depName),
invalidOnlyWarnsForCheck.createFor(depName),
if (depPkg.dependencies) {
allDirectDependenciesDependencies.push(
...Object.entries(depPkg.dependencies),
);
}
}
}),
);

for (const { name: depName, type: depType, pkg: depPkg } of allDepPkgs) {
if (depPkg.peerDependencies) {
checkPeerDependencies(
pkg,
reportError,
depType,
getAllowedPeerInFromType(depType, isLibrary),
allDirectDependenciesDependencies,
depPkg,
missingOnlyWarnsForCheck.createFor(depName),
invalidOnlyWarnsForCheck.createFor(depName),
);
}
}

reportNotWarnedForMapping(reportError, missingOnlyWarnsForCheck);
if (missingOnlyWarnsForCheck !== invalidOnlyWarnsForCheck) {
reportNotWarnedForMapping(reportError, invalidOnlyWarnsForCheck);
Expand Down
12 changes: 12 additions & 0 deletions src/checks/checkPeerDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function checkPeerDependencies(
reportError: ReportError,
type: DependencyTypes,
allowedPeerIn: DependencyTypes[],
providedDependencies: [string, string][],
depPkg: PackageJson,
missingOnlyWarnsForCheck: OnlyWarnsForCheck,
invalidOnlyWarnsForCheck: OnlyWarnsForCheck,
Expand All @@ -29,6 +30,17 @@ export function checkPeerDependencies(
if (peerDependenciesMetaPeerDep?.optional) {
continue;
}

// satisfied by another direct dependency
if (
providedDependencies.some(
([depName, depRange]) =>
depName === peerDepName && semver.intersects(range, depRange),
)
) {
continue;
}

reportError(
`Missing "${peerDepName}" peer dependency from "${depPkg.name}" in ${type}`,
`it should satisfies "${range}" and be in ${allowedPeerIn.join(
Expand Down

0 comments on commit 2aba256

Please sign in to comment.