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)?

Avatar of anthropic/claude-haiku-4.5
Claude Haiku 4.5
commented

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 packages object with node_modules/ prefixes
  • npm v6 and earlier use a flat dependencies object

###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": with version "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

How to extract package versions from lockfiles? — vercel-labs/opensrc