How to extract package versions from lockfiles?
Loading...
How do you extract/parse package versions from lockfiles (bun.lock, package-lock.json, yarn.lock, pnpm-lock.yaml)?
Great! Now I have a complete picture. Let me summarize how the opensrc project extracts and parses package versions from lockfiles:
##Lockfile Version Extraction Strategy
The project uses a priority-based approach to detect installed package versions:
1. node_modules (most accurate)
2. Lockfiles (in order: package-lock.json, pnpm-lock.yaml, yarn.lock)
3. package.json (fallback)
###1. package-lock.json (npm)
// Supports both npm v7+ and v6 formats
// npm v7+: looks in "packages" using "node_modules/{packageName}"
// npm v6: looks in "dependencies.{packageName}"
const lock = JSON.parse(content);
if (lock.packages) {
const key = `node_modules/${packageName}`;
if (lock.packages[key]?.version) {
return lock.packages[key].version;
}
}
if (lock.dependencies?.[packageName]?.version) {
return lock.dependencies[packageName].version;
}
Key Points:
- It's valid JSON, so simple
JSON.parse() - npm v7+ uses a nested
packagesobject withnode_modules/prefixes - npm v6 and earlier use a flat
dependenciesobject
###2. pnpm-lock.yaml (pnpm)
// Uses regex pattern matching since it's YAML text
const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const regex = new RegExp(`['"']?${escapedName}@([^(':"\\\s)]+)`, "g");
const matches = [...content.matchAll(regex)];
if (matches.length > 0) {
return matches[0][1]; // Return first match's version
}
Key Points:
- Format:
'packageName@version(peer-deps):or'packageName@version:' - Uses regex to extract the version between
@and punctuation ((,),:, quotes) - Handles peer dependencies like
ai@6.0.6(zod@4.3.4)by stopping at(or) - Returns the first occurrence
###3. yarn.lock (Yarn)
// Also regex-based since it's text format
const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const regex = new RegExp(
`"?${escapedName}@[^":\n]+[":]?\s*\n\s*version\s+["']?([^"'\n]+)`,
"g",
);
const matches = [...content.matchAll(regex)];
if (matches.length > 0) {
return matches[0][1];
}
Key Points:
- Format:
"packageName@^version":withversion "actual-version"on next line - Regex captures multi-line pattern: package line + version line
- Extracts the actual version value
###4. package.json (Fallback)
// Simple JSON parsing
const version =
pkg.dependencies?.[packageName] ||
pkg.devDependencies?.[packageName] ||
pkg.peerDependencies?.[packageName];
// Strip prefixes like ^, ~, >=, <, etc.
return version.replace(/^[\^~>=<]+/, "");
Key Points:
- Strips version range prefixes (
^,~,>=, etc.) - Falls back if lockfiles aren't present
###No Bun Support Yet
The codebase doesn't currently handle bun.lock files. Bun uses a binary format for its lockfile, so implementation would differ significantly from the text/JSON-based approaches above.
This approach is pragmatic—it prioritizes the most reliable source (what's actually installed) and gracefully falls back through increasingly less reliable sources.
END OF POST