const fs = require("fs"); const path = require("path"); function readJSON(p) { return JSON.parse(fs.readFileSync(p, "utf8")); } function getInstalledVersion(pkgName) { try { const parts = pkgName.split("/"); const pkgPath = path.join("node_modules", ...parts, "package.json"); const meta = readJSON(pkgPath); return meta.version; } catch (e) { return null; } } function collect(sectionName, obj) { if (!obj) return []; return Object.entries(obj).map(([name, declared]) => { const installed = getInstalledVersion(name); return { section: sectionName, name, from: declared, to: installed || null, strategy: "installed", }; }); } function main() { // If node_modules is missing, skip to avoid generating noisy diffs if (!fs.existsSync("node_modules")) { console.log("Skip: node_modules not found. Run npm install first."); return; } const pkg = readJSON("package.json"); const entries = [ ...collect("dependencies", pkg.dependencies), ...collect("devDependencies", pkg.devDependencies), ]; // Stable sort by section then name entries.sort((a, b) => a.section === b.section ? a.name.localeCompare(b.name) : a.section.localeCompare(b.section), ); const outPath = path.join(process.cwd(), "dependency-log.json"); // Merge with previous to preserve generatedAt when unchanged let prevMap = new Map(); if (fs.existsSync(outPath)) { try { const prev = JSON.parse(fs.readFileSync(outPath, "utf8")); for (const e of prev) { prevMap.set(`${e.section}::${e.name}`, e); } } catch {} } const now = new Date().toISOString(); const merged = entries.map((e) => { const key = `${e.section}::${e.name}`; const prev = prevMap.get(key); if (!prev) { // New entry: keep declared as from return { ...e, generatedAt: now }; } // If installed version changed, set from to previous installed version if (prev.to !== e.to) { return { ...e, from: prev.to, generatedAt: now }; } // Otherwise preserve previous 'from' and timestamp return { ...e, from: prev.from, generatedAt: prev.generatedAt || now }; }); const nextContent = JSON.stringify(merged, null, 2) + "\n"; if (fs.existsSync(outPath)) { try { const prevContent = fs.readFileSync(outPath, "utf8"); if (prevContent === nextContent) { console.log(`No changes for ${outPath} (entries: ${entries.length}).`); return; } } catch {} } fs.writeFileSync(outPath, nextContent); console.log(`Updated ${outPath} with ${entries.length} entries.`); } main();