claudes-office
Version:
CLI tool to initialize Claude's office in your project
274 lines • 9.61 kB
JavaScript
;
/**
* File comparison utilities for the update command
* These utilities help detect differences between files and directories
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.compareFiles = compareFiles;
exports.compareDirectories = compareDirectories;
exports.formatDifferences = formatDifferences;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const diff = __importStar(require("diff"));
/**
* Compare two files and return the differences
* @param filePath1 - Path to the first file
* @param filePath2 - Path to the second file
* @param options - Comparison options
* @returns The comparison result
*/
async function compareFiles(filePath1, filePath2, options = {}) {
try {
// Read file contents
const content1 = await fs.readFile(filePath1, 'utf8');
const content2 = await fs.readFile(filePath2, 'utf8');
// Normalize content if needed
let normalizedContent1 = content1;
let normalizedContent2 = content2;
if (options.ignoreWhitespace) {
normalizedContent1 = normalizedContent1.replace(/\s+/g, ' ').trim();
normalizedContent2 = normalizedContent2.replace(/\s+/g, ' ').trim();
}
if (options.ignoreCase) {
normalizedContent1 = normalizedContent1.toLowerCase();
normalizedContent2 = normalizedContent2.toLowerCase();
}
// Calculate differences
const differences = diff.diffLines(normalizedContent1, normalizedContent2);
// Calculate statistics
const stats = {
additions: 0,
deletions: 0,
changes: 0,
};
differences.forEach(part => {
if (part.added) {
stats.additions += part.count || 0;
}
else if (part.removed) {
stats.deletions += part.count || 0;
}
});
// If there are both additions and deletions, count them as changes
const minChanges = Math.min(stats.additions, stats.deletions);
stats.changes = minChanges;
stats.additions -= minChanges;
stats.deletions -= minChanges;
return {
identical: differences.length === 1 && !differences[0].added && !differences[0].removed,
differences,
stats,
};
}
catch (error) {
// If one of the files doesn't exist, they are not identical
if (error.code === 'ENOENT') {
return {
identical: false,
differences: [{
value: 'One or both files do not exist',
added: false,
removed: false,
}],
stats: {
additions: 0,
deletions: 0,
changes: 0,
},
};
}
throw error;
}
}
/**
* Compare two directories and return the differences
* @param dir1 - Path to the first directory
* @param dir2 - Path to the second directory
* @param options - Comparison options
* @returns The comparison result
*/
async function compareDirectories(dir1, dir2, options = {}) {
// Get all items in both directories recursively
const items1 = await getDirectoryItems(dir1);
const items2 = await getDirectoryItems(dir2);
// Organize items by relative path
const itemMap1 = new Map();
const itemMap2 = new Map();
items1.forEach(item => itemMap1.set(item.relativePath, item));
items2.forEach(item => itemMap2.set(item.relativePath, item));
// Find items in only one directory
const onlyInDir1 = [];
const onlyInDir2 = [];
const inBothDirs = [];
// Items only in dir1
items1.forEach(item => {
if (!itemMap2.has(item.relativePath)) {
// Skip ignored patterns
if (shouldIgnore(item.relativePath, options.ignorePatterns)) {
return;
}
onlyInDir1.push(item);
}
else {
inBothDirs.push(item);
}
});
// Items only in dir2
items2.forEach(item => {
if (!itemMap1.has(item.relativePath)) {
// Skip ignored patterns
if (shouldIgnore(item.relativePath, options.ignorePatterns)) {
return;
}
onlyInDir2.push(item);
}
});
// Compare files that exist in both directories
const differentFiles = [];
for (const item of inBothDirs) {
if (!item.isDirectory) {
// Skip ignored patterns
if (shouldIgnore(item.relativePath, options.ignorePatterns)) {
continue;
}
const filePath1 = path.join(dir1, item.relativePath);
const filePath2 = path.join(dir2, item.relativePath);
const comparison = await compareFiles(filePath1, filePath2, options);
if (!comparison.identical) {
differentFiles.push({
relativePath: item.relativePath,
comparison,
});
}
}
}
return {
onlyInDir1,
onlyInDir2,
inBothDirs,
differentFiles,
};
}
/**
* Get all items in a directory recursively
* @param dirPath - Path to the directory
* @param basePath - Base path for calculating relative paths
* @returns Array of directory items
*/
async function getDirectoryItems(dirPath, basePath = dirPath) {
const items = [];
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const entryPath = path.join(dirPath, entry.name);
const relativePath = path.relative(basePath, entryPath);
if (entry.isDirectory()) {
items.push({
path: entryPath,
isDirectory: true,
relativePath,
});
// Recursively process subdirectories
const subItems = await getDirectoryItems(entryPath, basePath);
items.push(...subItems);
}
else {
items.push({
path: entryPath,
isDirectory: false,
relativePath,
});
}
}
}
catch (error) {
// If directory doesn't exist, return empty array
if (error.code === 'ENOENT') {
return [];
}
throw error;
}
return items;
}
/**
* Check if a path should be ignored based on ignore patterns
* @param relativePath - Relative path to check
* @param ignorePatterns - Patterns to ignore
* @returns Whether the path should be ignored
*/
function shouldIgnore(relativePath, ignorePatterns) {
if (!ignorePatterns || ignorePatterns.length === 0) {
return false;
}
// Simple glob pattern matching
// For a real implementation, you'd want to use a proper glob library
return ignorePatterns.some(pattern => {
// Exact match
if (pattern === relativePath) {
return true;
}
// Directory wildcard (e.g., "dir/**")
if (pattern.endsWith('/**') && relativePath.startsWith(pattern.slice(0, -3))) {
return true;
}
// File extension wildcard (e.g., "*.js")
if (pattern.startsWith('*.') && relativePath.endsWith(pattern.slice(1))) {
return true;
}
return false;
});
}
/**
* Format differences for human-readable output
* @param differences - Array of diff changes
* @returns Formatted string with differences
*/
function formatDifferences(differences) {
let result = '';
differences.forEach(part => {
// Add prefix for added/removed lines
const prefix = part.added ? '+ ' : part.removed ? '- ' : ' ';
// Split by lines and add prefix to each line
const lines = part.value.split('\n');
lines.forEach(line => {
if (line) {
result += prefix + line + '\n';
}
});
});
return result;
}
//# sourceMappingURL=fileComparison.js.map