Skip to content

Commit 681a80b

Browse files
fix(peerDevDependencies): Use an array of package names in 'peerDevDependencies' in conjunction with the standard 'peerDependencies' object to install peer deps as devDependencies.
This now acts more as an extension of thstandard node packaging rather than adding completely custom behavior. Also removed check-peer-dependencies-optional-dependency because its postinstall script only gets runs when check-peer-dependencies-optional-dependency package is re-installed. So it was basically a failed experiment.
1 parent 8035295 commit 681a80b

File tree

7 files changed

+71
-147
lines changed

7 files changed

+71
-147
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
### Features
55

6-
* **check-peer-dependencies-optional-dependency:** Added a workaround for yarn swallowing postinstall script output ([a9c9fdf](https://github.com/christopherthielen/check-peer-dependencies/commit/a9c9fdf))
76
* **peerDevDependencies:** Add support for 'peerDevDependencies' -- 'peerDependencies' that should be installed as 'devDependencies' ([47d40ef](https://github.com/christopherthielen/check-peer-dependencies/commit/47d40ef))
87

98

README.md

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,34 +26,41 @@ Options:
2626

2727
---
2828

29+
## Installing peerDependencies as devDependencies
30+
31+
If a package has a peerDependency that should be installed as a devDependency by,
32+
it can list the package name in "peerDevDependencies".
33+
This is not a standard and is only understood by this `check-peer-dependencies`.
34+
35+
```json
36+
{
37+
"name": "somepackage",
38+
"peerDependencies": {
39+
"react": "16.x",
40+
"react-dom": "16.x",
41+
"typescript": "~3.8.0",
42+
"eslint": "*"
43+
},
44+
"peerDevDependencies": ["typescript", "eslint"]
45+
}
46+
```
47+
2948
## Example outputs:
3049

3150
### No problems
3251

3352
```bash
3453
~/projects/uirouter/sample-app-react master
3554
❯ npx check-peer-dependencies
36-
✅ @uirouter/visualizer@6.0.2 requires @uirouter/core >=5.0.0 (5.0.23 is installed)
37-
✅ ajv-keywords@3.4.1 requires ajv ^6.9.1 (6.10.2 is installed)
38-
✅ @uirouter/react@0.8.9 requires react ^16.3.0 (16.10.1 is installed)
39-
✅ react-dom@16.10.1 requires react ^16.0.0 (16.10.1 is installed)
40-
✅ file-loader@1.1.11 requires webpack ^2.0.0 || ^3.0.0 || ^4.0.0 (4.39.1 is installed)
41-
No problems found!
55+
✅ All peer dependencies are met
4256
```
4357

4458
### Missing peer dependency, solution found
4559

4660
```bash
4761
~/projects/uirouter/angular-hybrid master ⇣
4862
❯ npx check-peer-dependencies
49-
✅ @uirouter/angular@5.0.0 requires @angular/common ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (5.2.11 is installed)
50-
✅ @uirouter/angular-hybrid@9.0.0 requires @angular/core ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (5.2.11 is installed)
5163
❌ @uirouter/angular@5.0.0 requires @angular/router ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (@angular/router is not installed)
52-
✅ @uirouter/angular-hybrid@9.0.0 requires @angular/upgrade ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (5.2.11 is installed)
53-
✅ @uirouter/rx@0.6.0 requires @uirouter/core >=6.0.1 (6.0.1 is installed)
54-
✅ @uirouter/angular-hybrid@9.0.0 requires angular ^1.5.0 (1.7.8 is installed)
55-
✅ @uirouter/angularjs@1.0.23 requires angular >=1.2.0 (1.7.8 is installed)
56-
✅ @uirouter/rx@0.6.0 requires rxjs ^6.0.0 (6.5.3 is installed)
5764

5865
Searching for solutions:
5966
yarn add @angular/router@8.2.10
@@ -63,21 +70,9 @@ yarn add @angular/router@8.2.10
6370

6471
```bash
6572
❯ npx check-peer-dependencies
66-
✅ @angular/forms@9.0.0-next.9 requires @angular/common 9.0.0-next.9 (9.0.0-next.9 is installed)
6773
❌ @uirouter/angular@5.0.0 requires @angular/common ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (9.0.0-next.9 is installed)
68-
✅ @angular/platform-browser-dynamic@9.0.0-next.9 requires @angular/compiler 9.0.0-next.9 (9.0.0-next.9 is installed)
69-
✅ @angular/common@9.0.0-next.9 requires @angular/core 9.0.0-next.9 (9.0.0-next.9 is installed)
7074
❌ @uirouter/angular@5.0.0 requires @angular/core ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (9.0.0-next.9 is installed)
71-
✅ @angular/forms@9.0.0-next.9 requires @angular/platform-browser 9.0.0-next.9 (9.0.0-next.9 is installed)
7275
❌ @uirouter/angular@5.0.0 requires @angular/router ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 (9.0.0-next.9 is installed)
73-
✅ @uirouter/rx@0.6.0 requires @uirouter/core >=6.0.1 (6.0.1 is installed)
74-
✅ @uirouter/visualizer@6.0.2 requires @uirouter/core >=5.0.0 (6.0.1 is installed)
75-
✅ ajv-keywords@3.1.0 requires ajv ^6.0.0 (6.10.2 is installed)
76-
✅ @angular/common@9.0.0-next.9 requires rxjs ^6.5.3 (6.5.3 is installed)
77-
✅ @uirouter/rx@0.6.0 requires rxjs ^6.0.0 (6.5.3 is installed)
78-
✅ ts-helpers@1.1.2 requires typescript >=1.8.0 <2.1.0 || >=1.9.0-dev || >=2.0.0-dev || || >=2.1.0-dev (3.5.3 is installed)
79-
✅ file-loader@1.1.11 requires webpack ^2.0.0 || ^3.0.0 || ^4.0.0 (4.41.0 is installed)
80-
✅ @angular/core@9.0.0-next.9 requires zone.js ~0.10.2 (0.10.2 is installed)
8176

8277
Searching for solutions:
8378
❌ Unable to find a version of @angular/common that satisfies the following peerDependencies: 9.0.0-next.9 and ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -86,6 +81,3 @@ Searching for solutions:
8681
yarn upgrade @angular/router@8.2.10
8782
```
8883

89-
## Running as postinstall script using `yarn` package manager
90-
91-
Please see [check-peer-dependencies-optional-dependency](https://github.com/christopherthielen/check-peer-dependencies/tree/master/packages/check-peer-dependencies-optional-dependency)

packages/check-peer-dependencies-optional-dependency/README.md

Lines changed: 0 additions & 36 deletions
This file was deleted.

packages/check-peer-dependencies-optional-dependency/package.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/checkPeerDependencies.ts

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import * as semver from 'semver';
44
import { exec } from 'shelljs';
55
import { CliOptions } from './cli';
66
import { getCommandLines } from './packageManager';
7-
import { Dependency, gatherPeerDependencies, getInstalledVersion } from './packageUtils';
7+
import { Dependency, gatherPeerDependencies, getInstalledVersion, isSameDep } from './packageUtils';
88
import { findPossibleResolutions, Resolution } from './solution';
99

10-
function getAllNestedPeerDependencies(options: CliOptions) {
10+
function getAllNestedPeerDependencies(options: CliOptions): Dependency[] {
1111
const gatheredDependencies = gatherPeerDependencies(".", options);
1212

1313
function applySemverInformation(dep: Dependency): Dependency {
@@ -18,10 +18,7 @@ function getAllNestedPeerDependencies(options: CliOptions) {
1818
return { ...dep, installedVersion, semverSatisfies, isYalc };
1919
}
2020

21-
const allNestedPeerDependencies = gatheredDependencies.peerDependencies.map(applySemverInformation);
22-
const allNestedPeerDevDependencies = gatheredDependencies.peerDevDependencies.map(applySemverInformation);
23-
24-
return { allNestedPeerDependencies, allNestedPeerDevDependencies };
21+
return gatheredDependencies.map(applySemverInformation);
2522
}
2623

2724
let recursiveCount = 0;
@@ -55,18 +52,17 @@ const reportPeerDependencyStatusByDependee = (dep: Dependency, options: CliOptio
5552
};
5653

5754
export function checkPeerDependencies(packageManager: string, options: CliOptions) {
58-
const { allNestedPeerDependencies, allNestedPeerDevDependencies } = getAllNestedPeerDependencies(options);
59-
const combinedPeerAndPeerDevDependencies = [...allNestedPeerDependencies, ...allNestedPeerDevDependencies];
55+
const allNestedPeerDependencies = getAllNestedPeerDependencies(options);
6056

6157
if (options.orderBy === 'depender') {
62-
combinedPeerAndPeerDevDependencies.sort((a, b) => `${a.depender}${a.name}`.localeCompare(`${b.depender}${b.name}`));
63-
combinedPeerAndPeerDevDependencies.forEach(dep => reportPeerDependencyStatusByDepender(dep, options));
58+
allNestedPeerDependencies.sort((a, b) => `${a.depender}${a.name}`.localeCompare(`${b.depender}${b.name}`));
59+
allNestedPeerDependencies.forEach(dep => reportPeerDependencyStatusByDepender(dep, options));
6460
} else if (options.orderBy === 'dependee') {
65-
combinedPeerAndPeerDevDependencies.sort((a, b) => `${a.name}${a.depender}`.localeCompare(`${b.name}${b.depender}`));
66-
combinedPeerAndPeerDevDependencies.forEach(dep => reportPeerDependencyStatusByDependee(dep, options));
61+
allNestedPeerDependencies.sort((a, b) => `${a.name}${a.depender}`.localeCompare(`${b.name}${b.depender}`));
62+
allNestedPeerDependencies.forEach(dep => reportPeerDependencyStatusByDependee(dep, options));
6763
}
6864

69-
const problems = combinedPeerAndPeerDevDependencies.filter(dep => !dep.semverSatisfies && !dep.isYalc);
65+
const problems = allNestedPeerDependencies.filter(dep => !dep.semverSatisfies && !dep.isYalc);
7066

7167
if (!problems.length) {
7268
console.log(' ✅ All peer dependencies are met');
@@ -76,14 +72,14 @@ export function checkPeerDependencies(packageManager: string, options: CliOption
7672
console.log();
7773
console.log('Searching for solutions...');
7874
console.log();
79-
const resolutions: Resolution[] = findPossibleResolutions(problems, allNestedPeerDependencies, allNestedPeerDevDependencies);
75+
const resolutions: Resolution[] = findPossibleResolutions(problems, allNestedPeerDependencies);
8076
const resolutionsWithSolutions = resolutions.filter(r => r.resolution);
8177
const nosolution = resolutions.filter(r => !r.resolution);
8278

8379
nosolution.forEach(solution => {
8480
const name = solution.problem.name;
8581
const errorPrefix = `Unable to find a version of ${name} that satisfies the following peerDependencies:`;
86-
const peerDepRanges = combinedPeerAndPeerDevDependencies.filter(dep => dep.name === name)
82+
const peerDepRanges = allNestedPeerDependencies.filter(dep => dep.name === name)
8783
.reduce((acc, dep) => acc.includes(dep.version) ? acc : acc.concat(dep.version), []);
8884
console.error(` ❌ ${errorPrefix} ${peerDepRanges.join(" and ")}`)
8985
});
@@ -103,8 +99,7 @@ export function checkPeerDependencies(packageManager: string, options: CliOption
10399
console.log();
104100
});
105101

106-
const checkAgain = getAllNestedPeerDependencies(options);
107-
const newUnsatisfiedDeps = [...checkAgain.allNestedPeerDependencies, ...checkAgain.allNestedPeerDevDependencies]
102+
const newUnsatisfiedDeps = getAllNestedPeerDependencies(options)
108103
.filter(dep => !dep.semverSatisfies)
109104
.filter(dep => !nosolution.some(x => isSameDep(x.problem, dep)));
110105

@@ -131,19 +126,3 @@ export function checkPeerDependencies(packageManager: string, options: CliOption
131126
}
132127
process.exit(1);
133128
}
134-
135-
136-
function isSameDep(a: Dependency, b: Dependency) {
137-
const keys: Array<keyof Dependency> = [
138-
"name",
139-
"version",
140-
"depender",
141-
"dependerPath",
142-
"dependerVersion",
143-
"installedVersion",
144-
"semverSatisfies",
145-
"isYalc"
146-
];
147-
148-
return keys.every(key => a[key] === b[key]);
149-
}

src/packageUtils.ts

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ interface PackageJson {
1616
peerDependencies: {
1717
[key: string]: string;
1818
};
19-
// What is a peerDevDependency??!?! This is not a standard.
20-
// It is only supported by this tool as a means to specify peerDependencies to install as devDependencies.
19+
20+
// What is a peerDevDependency? This is not a standard.
21+
// This is an array of package names found in `peerDependencies` which should be installed as devDependencies.
2122
// This addresses a specific use case: to provide downstream projects with package building opinions such as
22-
// a specific version of rollup and typescript.
23-
peerDevDependencies: {
24-
[key: string]: string;
25-
};
23+
// a specific version of react and rollup and typescript.
24+
// Example:
25+
// peerDevDependencies: ["rollup", "typescript"]
26+
peerDevDependencies: string[];
2627
}
2728

2829
export interface Dependency {
@@ -34,49 +35,30 @@ export interface Dependency {
3435
installedVersion?: string | undefined;
3536
semverSatisfies?: boolean;
3637
isYalc?: boolean;
38+
isPeerDevDependency?: boolean;
3739
}
3840

3941
interface PackageDependencies {
4042
packageName: string;
4143
dependencies: Dependency[];
4244
devDependencies: Dependency[];
4345
peerDependencies: Dependency[];
44-
peerDevDependencies: Dependency[];
45-
}
46-
47-
interface GatheredDependencies {
48-
peerDependencies: Dependency[];
49-
peerDevDependencies: Dependency[];
46+
peerDevDependencies: string[];
5047
}
5148

5249
type DependencyWalkVisitor = (packagePath: string, packageJson: PackageJson, packageDependencies: PackageDependencies) => void;
5350

54-
export function gatherPeerDependencies(packagePath, options: CliOptions): GatheredDependencies {
55-
let peerDeps = [];
56-
let peerDevDeps = [];
51+
export function gatherPeerDependencies(packagePath, options: CliOptions): Dependency[] {
52+
let peerDeps: Dependency[] = [];
5753
const visitor: DependencyWalkVisitor = (path, json, deps) => {
5854
peerDeps = peerDeps.concat(deps.peerDependencies);
59-
peerDevDeps = peerDevDeps.concat(deps.peerDevDependencies);
6055
};
6156
walkPackageDependencyTree(packagePath, visitor, [], options);
6257

6358
// Eliminate duplicates
64-
const isSame = (dep: Dependency, dep2: Dependency) => {
65-
return dep.name === dep2.name
66-
&& dep.version === dep2.version
67-
&& dep.depender === dep2.depender
68-
&& dep.dependerVersion === dep2.dependerVersion;
69-
};
70-
71-
const peerDependencies = peerDeps.reduce((acc: Dependency[], dep: Dependency) => {
72-
return acc.some(dep2 => isSame(dep, dep2)) ? acc : acc.concat(dep);
73-
}, [] as Dependency[])
74-
75-
const peerDevDependencies = peerDevDeps.reduce((acc: Dependency[], dep: Dependency) => {
76-
return acc.some(dep2 => isSame(dep, dep2)) ? acc : acc.concat(dep);
77-
}, [] as Dependency[])
78-
79-
return { peerDependencies, peerDevDependencies };
59+
return peerDeps.reduce((acc: Dependency[], dep: Dependency) => {
60+
return acc.some(dep2 => isSameDep(dep, dep2)) ? acc : acc.concat(dep);
61+
}, [] as Dependency[]);
8062
}
8163

8264
export function walkPackageDependencyTree(packagePath: string, visitor: DependencyWalkVisitor, visitedPaths: string[], options: CliOptions) {
@@ -126,14 +108,17 @@ function buildDependencyArray(packagePath: string, packageJson: PackageJson, dep
126108
}
127109

128110
export function getPackageDependencies(packagePath: string, packageJson: PackageJson): PackageDependencies {
129-
const { name, dependencies = {}, devDependencies = {}, peerDependencies = {}, peerDevDependencies = {} } = packageJson;
111+
const { name, dependencies = {}, devDependencies = {}, peerDependencies = {}, peerDevDependencies = [] } = packageJson;
112+
113+
const applyPeerDevDependencies= (dep: Dependency): Dependency =>
114+
({ ...dep, isPeerDevDependency: peerDevDependencies.includes(dep.name) });
130115

131116
return {
132117
packageName: name,
133118
dependencies: buildDependencyArray(packagePath, packageJson, dependencies),
134119
devDependencies: buildDependencyArray(packagePath, packageJson, devDependencies),
135-
peerDependencies: buildDependencyArray(packagePath, packageJson, peerDependencies),
136-
peerDevDependencies: buildDependencyArray(packagePath, packageJson, peerDevDependencies),
120+
peerDependencies: buildDependencyArray(packagePath, packageJson, peerDependencies).map(applyPeerDevDependencies),
121+
peerDevDependencies,
137122
};
138123
}
139124

@@ -168,3 +153,20 @@ export function getInstalledVersion(dep: Dependency): string | undefined {
168153
const isYalc = fs.existsSync(path.resolve(peerDependencyDir, 'yalc.sig'));
169154
return isYalc ? `${packageJson.version}-yalc` : packageJson.version;
170155
}
156+
157+
158+
export function isSameDep(a: Dependency, b: Dependency) {
159+
const keys: Array<keyof Dependency> = [
160+
"name",
161+
"version",
162+
"depender",
163+
"dependerPath",
164+
"dependerVersion",
165+
"installedVersion",
166+
"semverSatisfies",
167+
"isYalc",
168+
"isPeerDevDependency",
169+
];
170+
171+
return keys.every(key => a[key] === b[key]);
172+
}

src/solution.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,11 @@ export interface Resolution {
1919
resolutionType: 'upgrade' | 'install' | 'devInstall';
2020
}
2121

22-
export function findPossibleResolutions(problems: Dependency[], peerDependencies: Dependency[], peerDevDependencies: Dependency[]): Resolution[] {
23-
const allPeerDependencies = [...peerDependencies, ...peerDevDependencies];
22+
export function findPossibleResolutions(problems: Dependency[], allPeerDependencies: Dependency[], ): Resolution[] {
2423
const uniq: Dependency[] = problems.reduce((acc, problem) => acc.some(dep => dep.name === problem.name) ? acc : acc.concat(problem), []);
2524
return uniq.map(problem => {
2625
const shouldUpgrade = !!problem.installedVersion;
27-
const isPeerDevDep = peerDevDependencies.some(dep => dep.name === problem.name);
28-
const resolutionType = shouldUpgrade ? 'upgrade' : isPeerDevDep ? 'devInstall' : 'install';
26+
const resolutionType = shouldUpgrade ? 'upgrade' : problem.isPeerDevDependency ? 'devInstall' : 'install';
2927
const resolutionVersion = findPossibleResolution(problem.name, allPeerDependencies);
3028
const resolution = resolutionVersion ? `${problem.name}@${resolutionVersion}` : null;
3129

0 commit comments

Comments
 (0)