-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First version, it works locally so what could go wrong? :stuck_out_tongue:
- Loading branch information
1 parent
854989e
commit eea2f2f
Showing
5 changed files
with
1,798 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,40 @@ | ||
# check-peer-deps | ||
Verifies that the peerDependency requirements of all top level dependencies are satisfied. | ||
Verifies that the `peerDependency` requirements of all top level dependencies | ||
are satisfied. | ||
|
||
## Installation | ||
|
||
You can install this on your system with a simple: | ||
|
||
```sh | ||
npm install check-peer-deps | ||
``` | ||
|
||
Please note that this utility requires `npm` to be available. | ||
|
||
## Usage | ||
|
||
Simply change into the directory of the project you wish to check the | ||
`peerDependencies` of and run the program. | ||
|
||
```sh | ||
> cd foobar | ||
> check-peer-deps | ||
``` | ||
|
||
If the minimum versions of all your top level `peerDependencies` are satisfied | ||
then there will be no output, otherwise you will see something similar to this: | ||
|
||
``` | ||
> check-peer-deps | ||
A [dev]Dependency satisfying eslint-config-airbnb-base's peerDependency of 'eslint@^4.9.0' was not found! | ||
Current: eslint@^4.6.0 | ||
Required version is allowed? Yes | ||
``` | ||
|
||
This tells you that `eslint-config-airbnb-base` is requiring `eslint@^4.9.0` as | ||
a `peerDependency`, but the project currently only specifies `eslint@^4.6.0`, | ||
allowing a potential issue to arise if `[email protected]` was installed and not | ||
updated before installing. The output also tells you that although the | ||
_minimum_ allowed version is too low, the _maximum_ allowed version does | ||
satisfy the `peerDependencies` requirement. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/usr/bin/env node | ||
|
||
process.title = 'check-peer-deps'; | ||
|
||
const checkPeerDeps = require('../check-peer-deps'); | ||
|
||
checkPeerDeps(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
const { exec } = require('sb-exec'); | ||
const semver = require('semver'); | ||
|
||
// Flags | ||
// Enable debug output | ||
const DEBUG = false; | ||
// Include development packages when checking whether a peerDependency has been | ||
// satisfied. | ||
const INCLUDE_DEV = true; | ||
// Use the local package.json files or check NPM to determine the peerDependencies | ||
const USE_LOCAL_PEERDEPS = true; | ||
|
||
// Internal vars | ||
const deps = new Map(); | ||
const npmVers = new Map(); | ||
const peerDeps = new Map(); | ||
|
||
const log = (value) => { | ||
if (DEBUG) { | ||
console.log(value); | ||
} | ||
}; | ||
|
||
const addDeps = (dependencies) => { | ||
Object.entries(dependencies).forEach((entry) => { | ||
const [name, range] = entry; | ||
deps.set(name, range); | ||
}); | ||
}; | ||
|
||
const gatherNpmVer = async (range, name) => { | ||
log(`Getting versions for ${name}@${range}...`); | ||
const opts = ['view', '--json', name, 'versions']; | ||
const versions = JSON.parse(await exec('npm', opts)); | ||
const ranges = { | ||
versions, | ||
minimum: semver.minSatisfying(versions, range), | ||
maximum: semver.maxSatisfying(versions, range), | ||
}; | ||
log(`${name}@${range}: '${ranges.minimum}' to '${ranges.maximum}'`); | ||
npmVers.set(name, ranges); | ||
}; | ||
|
||
const getNpmVersions = async () => { | ||
// Gather the unique package names | ||
const toCheck = new Set(); | ||
peerDeps.forEach((peerDependencies) => { | ||
peerDependencies.forEach((range, name) => { | ||
toCheck.add(name); | ||
}); | ||
}); | ||
|
||
// Grab the versions from NPM | ||
return Promise.all(Array.from(toCheck.values()).map(async (name) => { | ||
if (deps.has(name)) { | ||
await gatherNpmVer(deps.get(name), name); | ||
} | ||
})); | ||
}; | ||
|
||
// Get the peerDependencies | ||
const getNpmPeerDep = async (range, name) => { | ||
log(`Getting peerDependencies for ${name}`); | ||
const opts = ['view', '--json', name, 'peerDependencies']; | ||
const npmPeerDeps = JSON.parse(await exec('npm', opts)); | ||
if (!peerDeps.has(name)) { | ||
peerDeps.set(name, new Map()); | ||
} | ||
const currDeps = peerDeps.get(name); | ||
Object.entries(npmPeerDeps).forEach((entry) => { | ||
const [depName, depRange] = entry; | ||
log(`${depName}@${depRange}`); | ||
currDeps.set(depName, depRange); | ||
}); | ||
}; | ||
|
||
const getLocalPeerDep = async (range, name) => { | ||
log(`Getting peerDependencies for ${name}`); | ||
let packageInfo; | ||
try { | ||
// Hacktown, USA. | ||
// eslint-disable-next-line import/no-dynamic-require | ||
packageInfo = require(`${process.cwd()}/node_modules/${name}/package.json`); | ||
} catch (e) { | ||
return; | ||
} | ||
if (!packageInfo.peerDependencies) { | ||
return; | ||
} | ||
if (!peerDeps.has(name)) { | ||
peerDeps.set(name, new Map()); | ||
} | ||
const currDeps = peerDeps.get(name); | ||
Object.entries(packageInfo.peerDependencies).forEach((entry) => { | ||
const [depName, depRange] = entry; | ||
log(`${depName}@${depRange}`); | ||
currDeps.set(depName, depRange); | ||
}); | ||
}; | ||
|
||
const getPeerDeps = async () => { | ||
const promises = []; | ||
if (USE_LOCAL_PEERDEPS) { | ||
log("Using local package.json's to determine peerDependencies."); | ||
} else { | ||
log('Using NPM to determine peerDependencies.'); | ||
} | ||
deps.forEach((range, name) => { | ||
if (USE_LOCAL_PEERDEPS) { | ||
promises.push(getLocalPeerDep(range, name)); | ||
} else { | ||
promises.push(getNpmPeerDep(range, name)); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
}; | ||
|
||
// peerDependencies checks | ||
const checkPeerDependencies = async (peerDependencies, name) => | ||
Promise.all(Array.from(peerDependencies.entries()).map(async (entry) => { | ||
const [peerDepName, peerDepRange] = entry; | ||
log(`Checking ${name}'s peerDependency of '${peerDepName}@${peerDepRange}'`); | ||
let found = false; | ||
if (deps.has(peerDepName)) { | ||
// Verify that the minimum allowed version still satisfies the peerDep | ||
const minAllowedVer = npmVers.get(peerDepName).minimum; | ||
if (semver.satisfies(minAllowedVer, peerDepRange)) { | ||
found = true; | ||
} | ||
} | ||
|
||
if (!found) { | ||
console.error(`A ${INCLUDE_DEV ? '[dev]D' : 'd'}ependency satisfying ${name}'s peerDependency of '${peerDepName}@${peerDepRange}' was not found!`); | ||
|
||
if (deps.has(peerDepName)) { | ||
console.log(`Current: ${peerDepName}@${deps.get(peerDepName)}`); | ||
const { versions } = npmVers.get(peerDepName); | ||
const maxUsabe = semver.maxSatisfying(versions, peerDepRange); | ||
console.log(`Required version is allowed? ${maxUsabe ? 'Yes' : 'No'}`); | ||
} | ||
} | ||
})); | ||
|
||
const checkAllPeerDeps = async () => { | ||
const promises = []; | ||
peerDeps.forEach((peerDependencies, name) => { | ||
promises.push(checkPeerDependencies(peerDependencies, name)); | ||
}); | ||
return Promise.all(promises); | ||
}; | ||
|
||
// Main function | ||
async function checkPeerDeps() { | ||
// eslint-disable-next-line import/no-dynamic-require | ||
const packageConfig = require(`${process.cwd()}/package.json`); | ||
if (!packageConfig.dependencies) { | ||
console.error('No dependencies in the current pacakge!'); | ||
} | ||
|
||
// Get the dependencies to process | ||
addDeps(packageConfig.dependencies); | ||
|
||
if (INCLUDE_DEV && packageConfig.devDependencies) { | ||
addDeps(packageConfig.devDependencies); | ||
} | ||
|
||
log('Dependencies:'); | ||
deps.forEach((range, name) => { log(`${name}: ${range}`); }); | ||
|
||
log('Determining peerDependencies...'); | ||
await getPeerDeps(); | ||
log('Done.'); | ||
|
||
// Get the NPM versions required to check the peerDependencies | ||
log('Determining version ranges from NPM...'); | ||
await getNpmVersions(); | ||
log('Done.'); | ||
|
||
log('Checking versions...'); | ||
await checkAllPeerDeps(); | ||
log('Done.'); | ||
} | ||
|
||
module.exports = checkPeerDeps; |
Oops, something went wrong.