The registry diff library for comparing npm package versions via tarballs
npx @tessl/cli install tessl/npm-libnpmdiff@8.0.0libnpmdiff is a registry diff library that compares different versions of npm packages by fetching registry tarballs and generating unified diff outputs. It provides a Promise-based API for analyzing package changes with extensive configuration options for diff formatting and file filtering.
npm install libnpmdiffconst diff = require("libnpmdiff");const diff = require("libnpmdiff");
// Compare two package versions
const patch = await diff([
"abbrev@1.1.0",
"abbrev@1.1.1"
]);
console.log(patch);
// Compare with options
const coloredPatch = await diff([
"lodash@4.17.20",
"lodash@4.17.21"
], {
color: true,
diffUnified: 5,
diffFiles: ["package.json", "lib/**/*.js"]
});libnpmdiff follows a modular architecture with distinct phases for package comparison:
pacote.manifest() to resolve package specifications and extract version metadatagetTarball moduleuntar module extracts and filters files from tarballs based on configuration optionsformatDiff module generates unified diff patches using the diff library with customizable formattingminimatchThe library operates asynchronously throughout, using Promise-based APIs for all network operations and file processing. Each phase can be configured independently through the options parameter.
Compares two npm package specifications and returns a unified diff string.
/**
* Compare two npm package specifications and generate a unified diff
* @param {string[]} specs - Array of exactly 2 npm package specs to compare
* @param {DiffOptions} opts - Optional configuration options
* @returns {Promise<string>} Unified diff patch string
* @throws {TypeError} EDIFFARGS error if specs length is not exactly 2
*/
function diff(specs, opts = {});Parameters:
specs (Array): Array of exactly 2 npm package specifications. Supports all npm spec formats:
"package@1.0.0""package@^1.0.0""git+https://github.com/user/repo.git""file:/path/to/package""https://registry.npmjs.org/package/-/package-1.0.0.tgz"opts (Object, optional): Configuration options for diff generation
Returns: Promise that resolves to a string containing unified diff patches
Throws: TypeError with code 'EDIFFARGS' if specs array length is not exactly 2
The diff function accepts extensive configuration options:
interface DiffOptions {
// Diff formatting options
color?: boolean; // Add ANSI colors to output (default: false)
tagVersionPrefix?: string; // Prefix for version numbers (default: "v")
diffUnified?: number; // Lines of context before/after each diff (default: 3)
diffNameOnly?: boolean; // Print only file names, no patch content (default: false)
diffNoPrefix?: boolean; // Skip prefixes in filenames (default: false)
diffSrcPrefix?: string; // Prefix for source files (default: "a/")
diffDstPrefix?: string; // Prefix for destination files (default: "b/")
diffText?: boolean; // Treat all files as text, including binary (default: false)
diffIgnoreAllSpace?: boolean; // Ignore whitespace changes (default: false)
// File filtering options
diffFiles?: string[]; // Only show patches for specified files/globs
// Pacote options
cache?: string; // Cache directory path
registry?: string; // Registry URL
where?: string; // Working directory
// ... other pacote options
}const diff = require("libnpmdiff");
const patch = await diff([
"express@4.17.0",
"express@4.18.0"
]);
console.log(patch);const coloredDiff = await diff([
"react@17.0.0",
"react@18.0.0"
], {
color: true, // Enable ANSI colors
diffUnified: 5 // Show 5 lines of context
});const filtered = await diff([
"webpack@5.0.0",
"webpack@5.1.0"
], {
diffFiles: [
"package.json", // Specific file
"lib/**/*.js", // Glob pattern
"*.md" // All markdown files
]
});const fileNames = await diff([
"@babel/core@7.0.0",
"@babel/core@7.1.0"
], {
diffNameOnly: true // Only show changed file names
});const localDiff = await diff([
"file:/path/to/package1",
"file:/path/to/package2"
], {
diffNoPrefix: true, // Remove a/ and b/ prefixes
diffText: true // Force text mode for binary files
});const cleanDiff = await diff([
"prettier@2.0.0",
"prettier@2.1.0"
], {
diffIgnoreAllSpace: true, // Ignore whitespace changes
tagVersionPrefix: "" // Remove version prefix
});const diff = await diff([
"private-pkg@1.0.0",
"private-pkg@1.1.0"
], {
registry: "https://npm.example.com",
cache: "/tmp/npm-cache",
where: process.cwd()
});Thrown when the specs array doesn't contain exactly 2 elements:
try {
await diff(["single-package@1.0.0"]);
} catch (error) {
console.log(error.code); // "EDIFFARGS"
console.log(error.message); // "libnpmdiff needs two arguments to compare"
}Thrown when tarball extraction fails during processing:
try {
await diff(["corrupt-package@1.0.0", "corrupt-package@2.0.0"]);
} catch (error) {
console.log(error.code); // "EDIFFUNTAR"
console.log(error.message); // "failed to read files"
}// Error types
interface EDiffArgsError extends TypeError {
code: "EDIFFARGS";
message: "libnpmdiff needs two arguments to compare";
}
interface EDiffUntarError extends Error {
code: "EDIFFUNTAR";
message: "failed to read files";
}