Node JS directory compare library with extensive comparison options and TypeScript support
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Core comparison functionality for synchronous and asynchronous directory and file comparison operations.
Performs directory or file comparison synchronously and returns detailed results immediately.
/**
* Synchronously compares given paths.
* @param path1 Left file or directory to be compared
* @param path2 Right file or directory to be compared
* @param options Comparison options
* @returns Detailed comparison result with statistics and differences
*/
function compareSync(path1: string, path2: string, options?: Options): Result;Usage Examples:
import { compareSync, Options } from "dir-compare";
// Basic directory comparison
const result = compareSync("/path/to/dir1", "/path/to/dir2");
console.log(`Same: ${result.same}, Differences: ${result.differences}`);
// File comparison with content check
const options: Options = {
compareContent: true,
compareSize: true
};
const fileResult = compareSync("/path/to/file1.txt", "/path/to/file2.txt", options);
// Advanced comparison with filters
const advancedOptions: Options = {
compareContent: true,
compareDate: true,
excludeFilter: "*.log,temp/**",
includeFilter: "*.js,*.ts",
ignoreCase: true,
skipEmptyDirs: true
};
const filtered = compareSync("/src", "/backup/src", advancedOptions);Performs directory or file comparison asynchronously, ideal for large directory structures and UI applications.
/**
* Asynchronously compares given paths.
* @param path1 Left file or directory to be compared
* @param path2 Right file or directory to be compared
* @param options Comparison options
* @returns Promise resolving to detailed comparison result
*/
function compare(path1: string, path2: string, options?: Options): Promise<Result>;Usage Examples:
import { compare, Options } from "dir-compare";
// Basic async comparison
compare("/path/to/dir1", "/path/to/dir2")
.then(result => {
console.log(`Comparison complete: ${result.same ? 'identical' : 'different'}`);
console.log(`Files compared: ${result.totalFiles}`);
})
.catch(error => console.error("Comparison failed:", error));
// Async with await
async function compareDirectories() {
const options: Options = {
compareContent: true,
noDiffSet: false // Include detailed differences
};
try {
const result = await compare("/large/dir1", "/large/dir2", options);
// Process results
result.diffSet?.forEach(diff => {
if (diff.state !== 'equal') {
console.log(`${diff.relativePath}: ${diff.name1} vs ${diff.name2} - ${diff.state}`);
}
});
return result;
} catch (error) {
console.error("Comparison error:", error);
throw error;
}
}Comprehensive configuration options for customizing comparison behavior.
interface Options {
// Comparison strategies
compareSize?: boolean; // Compare files by size (default: false)
compareContent?: boolean; // Compare files by content (default: false)
compareDate?: boolean; // Compare files by modification date (default: false)
compareSymlink?: boolean; // Compare symlink values (default: false)
dateTolerance?: number; // Date comparison tolerance in ms (default: 1000)
// Directory traversal options
skipSubdirs?: boolean; // Skip subdirectories (default: false)
skipEmptyDirs?: boolean; // Ignore empty directories (default: false)
skipSymlinks?: boolean; // Ignore symbolic links (default: false)
// Name comparison
ignoreCase?: boolean; // Case-insensitive name comparison (default: false)
// Filtering
includeFilter?: string; // Comma-separated minimatch include patterns
excludeFilter?: string; // Comma-separated minimatch exclude patterns
// Performance and memory
noDiffSet?: boolean; // Exclude diffSet from results (default: false)
// Error handling
handlePermissionDenied?: boolean; // Continue on permission errors (default: false)
// Extension points
resultBuilder?: ResultBuilder; // Custom result builder
compareFileSync?: CompareFileSync; // Custom sync file comparator
compareFileAsync?: CompareFileAsync; // Custom async file comparator
compareNameHandler?: CompareNameHandler; // Custom name comparator
filterHandler?: FilterHandler; // Custom filter handler
// Additional properties for custom handlers
[key: string]: any;
}Detailed comparison results with comprehensive statistics and optional difference listing.
interface Result extends Statistics {
/**
* Detailed list of comparison results.
* Present if Options.noDiffSet is false (default).
*/
diffSet?: DiffSet;
}Result Processing Examples:
import { compareSync, Result } from "dir-compare";
const result: Result = compareSync("/dir1", "/dir2", { compareContent: true });
// Check if directories are identical
if (result.same) {
console.log("✅ Directories are identical");
} else {
console.log(`❌ Found ${result.differences} differences`);
// Analyze statistics
console.log(`Files: ${result.equalFiles} equal, ${result.distinctFiles} different`);
console.log(`Directories: ${result.equalDirs} equal, ${result.distinctDirs} different`);
console.log(`Left only: ${result.left}, Right only: ${result.right}`);
}
// Process individual differences
result.diffSet?.forEach(diff => {
switch (diff.state) {
case 'left':
console.log(`➖ Only in left: ${diff.relativePath}/${diff.name1}`);
break;
case 'right':
console.log(`➕ Only in right: ${diff.relativePath}/${diff.name2}`);
break;
case 'distinct':
console.log(`🔄 Different: ${diff.relativePath}/${diff.name1} - ${diff.reason}`);
break;
case 'equal':
// Usually filtered out, but available if needed
break;
}
});
// Handle special cases
if (result.brokenLinks.totalBrokenLinks > 0) {
console.log(`⚠️ Found ${result.brokenLinks.totalBrokenLinks} broken symlinks`);
}
if (result.permissionDenied.totalPermissionDenied > 0) {
console.log(`🔒 ${result.permissionDenied.totalPermissionDenied} permission denied entries`);
}import { compare, compareSync } from "dir-compare";
// Handle permission errors gracefully
const options = {
compareContent: true,
handlePermissionDenied: true // Continue on permission errors
};
try {
const result = await compare("/protected/dir1", "/protected/dir2", options);
// Check for permission issues
if (result.permissionDenied.totalPermissionDenied > 0) {
console.warn(`Could not access ${result.permissionDenied.totalPermissionDenied} entries`);
// Examine specific permission errors in diffSet
result.diffSet?.forEach(diff => {
if (diff.permissionDeniedState !== 'access-ok') {
console.log(`Permission denied: ${diff.relativePath} - ${diff.permissionDeniedState}`);
}
});
}
} catch (error) {
console.error("Fatal comparison error:", error);
}import { compare } from "dir-compare";
// For large directory comparisons
const largeDirectoryOptions = {
compareContent: true,
noDiffSet: true, // Save memory by excluding detailed differences
skipEmptyDirs: true, // Skip empty directories
excludeFilter: ".git,node_modules,*.log" // Exclude unnecessary files
};
// Memory-efficient comparison
const result = await compare("/very/large/dir1", "/very/large/dir2", largeDirectoryOptions);
console.log(`Processed ${result.total} entries, found ${result.differences} differences`);
// result.diffSet will be undefined to save memoryInstall with Tessl CLI
npx tessl i tessl/npm-dir-compare