Skip to content

Instantly share code, notes, and snippets.

@Nurou
Last active December 25, 2023 13:45
Show Gist options
  • Save Nurou/509a4e14021a83e3df3fde8318e4d57d to your computer and use it in GitHub Desktop.
Save Nurou/509a4e14021a83e3df3fde8318e4d57d to your computer and use it in GitHub Desktop.
Fetches, categories and CSV formats release data for npm packages. Skips < v1 and pre-releases (e.g. chores and betas).
const { promisify } = require("util");
const execAsync = promisify(require("child_process").exec);
const fs = require("fs");
async function getReleaseData(pkgName) {
const command = `npm view ${pkgName} time --json`;
try {
const { stdout } = await execAsync(command);
const data = JSON.parse(stdout);
return data;
} catch (error) {
console.error(`Error executing command: ${error.message}`);
throw error;
}
}
function preStabilityReleasesAndBetas(r) {
const [k, v] = r;
const parts = k.split("-");
if (parts.length > 1) return false;
const [major] = parts[0].split(".");
if (major === "0" || isNaN(parseInt(major))) return false;
return true;
}
function getVersionType(next, prev) {
const nextParts = next.split(".");
const prevParts = prev.split(".");
const nextMajor = parseInt(nextParts[0]);
const nextMinor = parseInt(nextParts[1]);
const nextPatch = parseInt(nextParts[2]);
const prevMajor = parseInt(prevParts[0]);
const prevMinor = parseInt(prevParts[1]);
const prevPatch = parseInt(prevParts[2]);
if (nextMajor > prevMajor) {
return "major";
} else if (nextMinor > prevMinor) {
return "minor";
} else if (nextPatch > prevPatch) {
return "patch";
} else {
console.log("unknown version type", next, prev);
return "unknown";
}
}
// e.g. "1.1.1"-> "1.11"
function convertToSingleDecimalForm(str) {
const parts = str.split(".");
const major = parts[0];
const minor = parts[1];
const patch = parts[2];
return `${major}.${minor}${patch}`;
}
function writeToCSV(releases, packageName) {
const csvData = [["version", "date", "type"]];
let previousVersion = "0.0.0";
for (const [version, dateStr] of Object.entries(releases)) {
const date = new Date(dateStr);
const formattedDate = date.toISOString().substring(0, 10);
const versionType = previousVersion
? getVersionType(version, previousVersion)
: "unknown";
const formattedVersion = convertToSingleDecimalForm(version);
csvData.push([formattedVersion, formattedDate, versionType]);
previousVersion = version;
}
const csvString = csvData.map((row) => row.join(",")).join("\n");
const filename = `${packageName}-releases.csv`;
fs.writeFileSync(filename, csvString, "utf-8");
console.log(`CSV data has been saved to ${filename}`);
}
function sortReleases(releases) {
return Object.entries(releases)
.filter(preStabilityReleasesAndBetas)
.sort(([, a], [, b]) => a - b) // sort by date
.reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
}
async function main() {
const pkgName = process.argv[2];
const releases = await getReleaseData(pkgName);
const sortedReleases = sortReleases(releases);
writeToCSV(sortedReleases, pkgName);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment