Skip to content

Instantly share code, notes, and snippets.

@bitsnaps
Last active May 10, 2024 13:51
Show Gist options
  • Save bitsnaps/93397b65ac867fcfaf8b18760884d658 to your computer and use it in GitHub Desktop.
Save bitsnaps/93397b65ac867fcfaf8b18760884d658 to your computer and use it in GitHub Desktop.
Find the most recent version of any given Node package by looking at every package.json file recursively in the current directory, I used this in order to detect the most recent version before downloading a new one instead of using `--prefer-offline` installation. It works offline.
#!/usr/bin/env node
/**
* This script finds the most recent installed version of any given Node package.
* Simple usage:
* node findRec.js vue [/path/to/projects]
*/
const fs = require('fs');
const os = require('os');
const path = require('path');
// Function to validate semver version strings
function isValidSemver(version) {
const semverRegex = /^\d+(\.\d+){0,2}$/;
return semverRegex.test(version);
}
// Function to recursively find package.json files
function findPackageJsons(dir, visited = new Set(), callback) {
if (typeof callback !== 'function') {
console.error('Callback must be a function');
return;
}
try {
if (visited.has(dir)) {
return; // Skip if this directory has already been visited
}
visited.add(dir); // Mark this directory as visited
fs.readdirSync(dir).forEach(file => {
const filePath = path.join(dir, file);
const stat = fs.lstatSync(filePath); // Use lstat to get info about the link itself, not the target
if (stat.isSymbolicLink()) {
return; // Skip symbolic links to avoid loops
}
if (stat.isDirectory() && file !== 'node_modules') {
findPackageJsons(filePath, visited, callback);
} else if (file === 'package.json') {
callback(filePath); // Call the callback with the path to the package.json file
}
});
} catch (err) {
if (err.code !== 'ENOENT') {
console.error(`Error reading directory ${dir}: ${err}`);
}
}
}
// Function to find the most recent version of a package
function findMostRecentVersion(currentDir, packageName, callback) {
let mostRecentVersion = null;
let directoryOfMostRecentVersion = null; // Store the directory of the most recent version
findPackageJsons(currentDir, new Set(), (packageJsonPath) => {
const packageJson = require(packageJsonPath);
const version = (packageJson.dependencies && packageJson.dependencies[packageName]) ||
(packageJson.devDependencies && packageJson.devDependencies[packageName]);
if (version) {
const cleanVersion = cleanVersionString(version);
console.log(`mostRecent: ${mostRecentVersion}, v: ${cleanVersion}, at: ${path.dirname(packageJsonPath).replace(os.homedir(),'')}`);
if (cleanVersion && (!mostRecentVersion || compareVersions(cleanVersion, mostRecentVersion) > 0)) {
mostRecentVersion = cleanVersion;
directoryOfMostRecentVersion = path.dirname(packageJsonPath); // Update the directory storing the most recent version
}
}
});
callback(mostRecentVersion, directoryOfMostRecentVersion); // Pass the directory along with the version to the callback
}
// Removes any range specifiers from the version string
function cleanVersionString(version) {
return version.replace(/^[^0-9]*/, '');
}
// Compare two semantic version strings, return -1, 0, or 1
function compareVersions(v1, v2) {
const parts1 = v1.split('.').map(Number);
const parts2 = v2.split('.').map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const num1 = parts1[i] || 0;
const num2 = parts2[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
// Get package name from command line arguments
const packageName = process.argv[2];
const currentDir = process.argv[3]?process.argv[3]:process.cwd();
if (!packageName) {
console.error('Please provide a package name as an argument.');
process.exit(1);
}
// Example usage
findMostRecentVersion(currentDir, packageName, (version, directory) => {
if (version) {
console.log(`The most recent version of ${packageName} found is ${version} in directory ${directory}`);
} else {
console.log(`Package ${packageName} not found in any package.json`);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment