Skip to content

Commit

Permalink
Add a script to lint dependencies (#770)
Browse files Browse the repository at this point in the history
Once we have all checks passing (#832), we can include it on our regular CI process
  • Loading branch information
alcuadrado authored Aug 13, 2020
1 parent dbc7ad7 commit 25254da
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions scripts/check-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
const fs = require("fs");
const path = require("path");

// An array of dependencies whose version checks are ignored for all the
// packages
const IGNORE_FROM_ALL = [
//"exampleDependency"
];

// A map from dependencies to package names where it should be ignored
const IGNORE_FOR_PACKAGES = {
//exampleDependency: ["examplePackage"]
};

function checkPeerDepedencies(packageJson) {
if (packageJson.peerDependencies === undefined) {
return true;
}

if (packageJson.devDependencies === undefined) {
console.error(
`${packageJson.name} has peerDependencies but no devDependencies`
);

return false;
}

let success = true;
for (const dependency of Object.keys(packageJson.peerDependencies)) {
if (packageJson.devDependencies[dependency] === undefined) {
console.error(
`${
packageJson.name
} has ${dependency} as peerDependency, but not as devDependency`
);

success = false;

continue;
}

if (
packageJson.peerDependencies[dependency] !==
packageJson.devDependencies[dependency]
) {
console.error(
`${
packageJson.name
} has different versions of ${dependency} as peerDependency and devDependency`
);

success = false;
}
}

return success;
}

function addDependencies(packageName, dependenciesToAdd, allDependenciesMap) {
if (dependenciesToAdd === undefined) {
return;
}

for (const [name, spec] of Object.entries(dependenciesToAdd)) {
if (IGNORE_FROM_ALL.includes(name)) {
continue;
}

if (
IGNORE_FOR_PACKAGES[name] !== undefined &&
IGNORE_FOR_PACKAGES[name].includes(packageName)
) {
continue;
}

if (allDependenciesMap[name] === undefined) {
allDependenciesMap[name] = {};
}

if (allDependenciesMap[name][spec] === undefined) {
allDependenciesMap[name][spec] = new Set();
}

allDependenciesMap[name][spec].add(packageName);
}
}

function getDependencyMap(packageJson) {
// Map of: dependencyName => versionSpec => set of module names
const dependencies = {};

addDependencies(packageJson.name, packageJson.dependencies, dependencies);
addDependencies(packageJson.name, packageJson.devDependencies, dependencies);
addDependencies(packageJson.name, packageJson.peerDependencies, dependencies);

return dependencies;
}

function mergeDependenciesMap(dependencyMaps) {
// Map of: dependencyName => versionSpec => set of module names
const dependencies = {};

for (const map of dependencyMaps) {
for (const [name, specs] of Object.entries(map)) {
if (dependencies[name] === undefined) {
dependencies[name] = {};
}

for (const spec of Object.keys(specs)) {
if (dependencies[name][spec] === undefined) {
dependencies[name][spec] = new Set();
}

for (const packageName of map[name][spec]) {
dependencies[name][spec].add(packageName);
}
}
}
}

return dependencies;
}

function getAllPackageJsonPaths() {
const packageNames = fs.readdirSync(path.join(__dirname, "..", "packages"));

const packageJsons = packageNames.map(p =>
path.join(__dirname, "..", "packages", p, "package.json")
);

packageJsons.push(path.join(__dirname, "..", "package.json"));

return packageJsons;
}

function main() {
let success = true;
const dependencyMaps = [];
for (const packageJsonPath of getAllPackageJsonPaths()) {
if (!fs.existsSync(packageJsonPath)) {
console.warn(`${packageJsonPath} doesn't exist, skipping it`);
continue;
}

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
const peersOk = checkPeerDepedencies(packageJson);
const dependencyMap = getDependencyMap(packageJson);
dependencyMaps.push(dependencyMap);

if (peersOk === false) {
success = false;
}
}

const allDependenciesMap = mergeDependenciesMap(dependencyMaps);

for (const dependency of Object.keys(allDependenciesMap)) {
if (Object.keys(allDependenciesMap[dependency]).length !== 1) {
console.error(`Incompatible versions of dependency: ${dependency}`);

for (const [spec, packageNames] of Object.entries(
allDependenciesMap[dependency]
)) {
console.log(` Packages with version ${spec}:`);

for (const packageName of packageNames) {
console.log(` ${packageName}`);
}
}

success = false;
}
}

if (success === false) {
process.exit(1);
}
}

main();

0 comments on commit 25254da

Please sign in to comment.