Skip to content

Instantly share code, notes, and snippets.

@lisadean
Created April 18, 2023 17:19
Show Gist options
  • Save lisadean/72856d118618448d05a60a7ca3b53848 to your computer and use it in GitHub Desktop.
Save lisadean/72856d118618448d05a60a7ca3b53848 to your computer and use it in GitHub Desktop.
Node script to compare two node_modules directories and return differences
const fs = require('fs');
const path = require('path');
// dir fergy linked
const dir1 = '/Users/abb2175/work/react-build-store-alt/node_modules';
// regular dir
const dir2 = '/Users/abb2175/work/react-build-store/node_modules';
// compareDirectories(dir1, dir2, 1);
const result = findUniqueFiles(dir1, dir2, 1);
console.log('Files/folders (', result.uniqueToDir1.size, ') unique to FWP linked dir:', dir1);
result.uniqueToDir1.forEach((item) => console.log(item));
console.log();
console.log('Files/folders unique to regular dir:', dir2);
// console.log(`Files/folders unique to ${dir2}:`);
result.uniqueToDir2.forEach((item) => console.log(item));
function filterByDepth(contents, depth, dir, basePath) {
if (depth === 1) {
return contents.filter((name) => {
const fullPath = path.join(dir, name);
const stats = fs.statSync(fullPath, fs.constants.FOLLOW_LINKS);
return stats.isDirectory() || stats.isFile();
});
} else if (depth > 1) {
return contents.flatMap((name) => {
const fullPath = path.join(dir, name);
const relPath = path.relative(basePath, fullPath);
const stats = fs.statSync(fullPath, fs.constants.FOLLOW_LINKS);
if (stats.isDirectory()) {
const subContents = fs.readdirSync(fullPath);
const subFiltered = filterByDepth(subContents, depth - 1, fullPath, basePath);
return subFiltered.map((subName) => path.join(relPath, subName));
} else {
return [relPath];
}
});
} else {
return contents.map((name) => path.relative(basePath, path.join(dir, name)));
}
}
function findUniqueFiles(dir1, dir2, depth) {
const excludedFiles = findDevDependencies(dir1);
const dir1Contents = fs.readdirSync(dir1);
const dir2Contents = fs.readdirSync(dir2);
const filteredDir1 = filterByDepth(dir1Contents, depth, dir1, dir1).filter((name) => !excludedFiles.has(name));
const filteredDir2 = filterByDepth(dir2Contents, depth, dir2, dir2);
return {
uniqueToDir1: getUniqueFiles(filteredDir1, filteredDir2),
uniqueToDir2: getUniqueFiles(filteredDir2, filteredDir1)
};
}
function findDevDependencies(dir) {
const excludedFiles = new Set();
const packageJsonPath = path.join(dir, 'package.json');
const packageLockPath = path.join(dir, 'package-lock.json');
try {
const packageJsonData = fs.readFileSync(packageJsonPath);
const packageJson = JSON.parse(packageJsonData);
const devDeps = packageJson.devDependencies || {};
const allDeps = new Set(Object.keys(devDeps));
// Add all dependencies of the dev dependencies to the set of excluded files
if (fs.existsSync(packageLockPath)) {
const packageLockData = fs.readFileSync(packageLockPath);
const packageLock = JSON.parse(packageLockData);
Object.keys(devDeps).forEach((dep) => {
const depInfo = packageLock.dependencies[dep];
if (depInfo && depInfo.requires) {
Object.keys(depInfo.requires).forEach((req) => {
allDeps.add(req);
});
}
});
}
// Add the path of each dependency to the set of excluded files
allDeps.forEach((dep) => {
excludedFiles.add(path.join('node_modules', dep));
});
} catch (err) {
// Ignore errors when reading or parsing package.json or package-lock.json files
}
// Recursively search subdirectories
const subdirs = fs
.readdirSync(dir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
subdirs.forEach((subdir) => {
const subExcludedFiles = findDevDependencies(path.join(dir, subdir));
subExcludedFiles.forEach((file) => excludedFiles.add(file));
});
return excludedFiles;
}
function getUniqueFiles(files1, files2) {
const files1Set = new Set(files1);
const files2Set = new Set(files2);
return [...files1Set].filter((file) => !files2Set.has(file));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment