File content comparison handlers for binary and text-based comparison strategies with extensive customization options.
Collection of pre-built file comparison handlers for different comparison strategies.
interface FileCompareHandlers {
/**
* Binary file content comparator (default).
* Compares files byte-by-byte for exact binary equality.
*/
defaultFileCompare: CompareFileHandler;
/**
* Line-based text file comparator with whitespace and line-ending options.
* Supports ignoring whitespace differences, line endings, and empty lines.
*/
lineBasedFileCompare: CompareFileHandler;
}
interface CompareFileHandler {
compareSync: CompareFileSync;
compareAsync: CompareFileAsync;
}Binary comparison for exact file content matching.
/**
* Synchronous binary file comparison
* @param path1 Left file path
* @param stat1 Left file stats
* @param path2 Right file path
* @param stat2 Right file stats
* @param options Comparison options
* @returns true if files are identical, false otherwise
*/
type CompareFileSync = (
path1: string,
stat1: fs.Stats,
path2: string,
stat2: fs.Stats,
options: Options
) => boolean;
/**
* Asynchronous binary file comparison
* @param path1 Left file path
* @param stat1 Left file stats
* @param path2 Right file path
* @param stat2 Right file stats
* @param options Comparison options
* @returns Promise resolving to true if files are identical
*/
type CompareFileAsync = (
path1: string,
stat1: fs.Stats,
path2: string,
stat2: fs.Stats,
options: Options
) => Promise<boolean>;Usage Examples:
import { compareSync, fileCompareHandlers } from "dir-compare";
// Use default binary comparator (this is the default behavior)
const binaryResult = compareSync("/dir1", "/dir2", {
compareContent: true,
compareFileSync: fileCompareHandlers.defaultFileCompare.compareSync,
compareFileAsync: fileCompareHandlers.defaultFileCompare.compareAsync
});
// Binary comparison is exact - any byte difference results in 'distinct'
console.log(`Binary comparison found ${binaryResult.distinctFiles} different files`);Advanced text comparison with options to ignore whitespace and line-ending differences.
interface LineBasedOptions extends Options {
/**
* Ignore cr/lf line ending differences (default: false)
* Similar to 'diff --strip-trailing-cr'
*/
ignoreLineEnding?: boolean;
/**
* Ignore white spaces at beginning and end of lines (default: false)
* Similar to 'diff -b'
*/
ignoreWhiteSpaces?: boolean;
/**
* Ignore all white space differences (default: false)
* Similar to 'diff -w'
*/
ignoreAllWhiteSpaces?: boolean;
/**
* Ignore differences caused by empty lines (default: false)
* Similar to 'diff -B'
*/
ignoreEmptyLines?: boolean;
}Usage Examples:
import { compareSync, compare, fileCompareHandlers } from "dir-compare";
// Basic line-based comparison
const textOptions = {
compareContent: true,
compareFileSync: fileCompareHandlers.lineBasedFileCompare.compareSync,
compareFileAsync: fileCompareHandlers.lineBasedFileCompare.compareAsync,
ignoreLineEnding: true, // Ignore CRLF vs LF differences
ignoreWhiteSpaces: true // Ignore leading/trailing whitespace
};
const textResult = compareSync("/src", "/backup/src", textOptions);
// Advanced text comparison ignoring various whitespace differences
const advancedTextOptions = {
compareContent: true,
compareFileSync: fileCompareHandlers.lineBasedFileCompare.compareSync,
compareFileAsync: fileCompareHandlers.lineBasedFileCompare.compareAsync,
ignoreLineEnding: true, // Ignore line ending differences
ignoreWhiteSpaces: true, // Ignore leading/trailing spaces
ignoreAllWhiteSpaces: true, // Ignore all whitespace differences
ignoreEmptyLines: true, // Ignore empty line differences
includeFilter: "*.txt,*.md,*.js,*.ts" // Only compare text files
};
const flexibleResult = await compare("/docs1", "/docs2", advancedTextOptions);
// Process results focusing on content differences
flexibleResult.diffSet?.forEach(diff => {
if (diff.state === 'distinct' && diff.reason === 'different-content') {
console.log(`Content differs: ${diff.relativePath}/${diff.name1}`);
}
});Create custom file comparison logic for specialized needs.
/**
* Custom file comparator example for JSON files
*/
function createJsonComparator(): CompareFileHandler {
const compareJsonSync: CompareFileSync = (path1, stat1, path2, stat2, options) => {
try {
const content1 = fs.readFileSync(path1, 'utf8');
const content2 = fs.readFileSync(path2, 'utf8');
const json1 = JSON.parse(content1);
const json2 = JSON.parse(content2);
return JSON.stringify(json1) === JSON.stringify(json2);
} catch (error) {
// Fall back to binary comparison on parse errors
return fileCompareHandlers.defaultFileCompare.compareSync(path1, stat1, path2, stat2, options);
}
};
const compareJsonAsync: CompareFileAsync = async (path1, stat1, path2, stat2, options) => {
try {
const [content1, content2] = await Promise.all([
fs.promises.readFile(path1, 'utf8'),
fs.promises.readFile(path2, 'utf8')
]);
const json1 = JSON.parse(content1);
const json2 = JSON.parse(content2);
return JSON.stringify(json1) === JSON.stringify(json2);
} catch (error) {
return fileCompareHandlers.defaultFileCompare.compareAsync(path1, stat1, path2, stat2, options);
}
};
return {
compareSync: compareJsonSync,
compareAsync: compareJsonAsync
};
}Usage with Custom Comparators:
import { compareSync } from "dir-compare";
// Use custom JSON comparator
const jsonComparator = createJsonComparator();
const jsonComparisonOptions = {
compareContent: true,
compareFileSync: jsonComparator.compareSync,
compareFileAsync: jsonComparator.compareAsync,
includeFilter: "*.json" // Only compare JSON files
};
const result = compareSync("/config1", "/config2", jsonComparisonOptions);
// Custom size-based comparator (files equal if size matches)
const sizeOnlyComparator: CompareFileHandler = {
compareSync: (path1, stat1, path2, stat2, options) => {
return stat1.size === stat2.size;
},
compareAsync: async (path1, stat1, path2, stat2, options) => {
return stat1.size === stat2.size;
}
};
const sizeBasedResult = compareSync("/images1", "/images2", {
compareContent: true,
compareFileSync: sizeOnlyComparator.compareSync,
includeFilter: "*.jpg,*.png"
});import { compare, fileCompareHandlers } from "dir-compare";
// For large files, consider size pre-check
const efficientOptions = {
compareSize: true, // Quick size check first
compareContent: true, // Only do content comparison if sizes match
compareFileAsync: fileCompareHandlers.defaultFileCompare.compareAsync,
excludeFilter: "*.iso,*.dmg,*.zip" // Exclude large binary files
};
// Line-based comparison is slower but more flexible for text files
const textFileOptions = {
compareContent: true,
compareFileAsync: fileCompareHandlers.lineBasedFileCompare.compareAsync,
ignoreWhiteSpaces: true,
ignoreLineEnding: true,
includeFilter: "*.txt,*.md,*.js,*.ts,*.html,*.css" // Text files only
};
const mixedResult = await compare("/project1", "/project2", {
compareSize: true, // Fast initial check
compareContent: true, // Detailed content check
// Use default binary comparator (will be set automatically)
});import { compareSync, fileCompareHandlers } from "dir-compare";
// Robust comparison with error handling
const robustOptions = {
compareContent: true,
handlePermissionDenied: true, // Continue on access errors
compareFileSync: (path1, stat1, path2, stat2, options) => {
try {
// Use default binary comparator
return fileCompareHandlers.defaultFileCompare.compareSync(path1, stat1, path2, stat2, options);
} catch (error) {
console.warn(`Failed to compare ${path1} and ${path2}:`, error.message);
// Consider files different if comparison fails
return false;
}
}
};
const result = compareSync("/fragile/dir1", "/fragile/dir2", robustOptions);
// Check for comparison errors
result.diffSet?.forEach(diff => {
if (diff.permissionDeniedState !== 'access-ok') {
console.log(`Access error: ${diff.relativePath} - ${diff.permissionDeniedState}`);
}
});