var fs = require('fs') var entryBuilder = require('./entry/entryBuilder') var entryEquality = require('./entry/entryEquality') var stats = require('./statistics/statisticsUpdate') var pathUtils = require('path') var fsPromise = require('./fs/fsPromise') var loopDetector = require('./symlink/loopDetector') var entryComparator = require('./entry/entryComparator') var entryType = require('./entry/entryType') /** * Returns the sorted list of entries in a directory. */ var getEntries = function (rootEntry, relativePath, loopDetected, options) { if (!rootEntry || loopDetected) { return Promise.resolve([]) } if (rootEntry.isDirectory) { return fsPromise.readdir(rootEntry.absolutePath) .then(function (entries) { return entryBuilder.buildDirEntries(rootEntry, entries, relativePath, options) }) } return Promise.resolve([rootEntry]) } /** * Compares two directories asynchronously. */ var compare = function (rootEntry1, rootEntry2, level, relativePath, options, statistics, diffSet, symlinkCache) { var loopDetected1 = loopDetector.detectLoop(rootEntry1, symlinkCache.dir1) var loopDetected2 = loopDetector.detectLoop(rootEntry2, symlinkCache.dir2) loopDetector.updateSymlinkCache(symlinkCache, rootEntry1, rootEntry2, loopDetected1, loopDetected2) return Promise.all([getEntries(rootEntry1, relativePath, loopDetected1, options), getEntries(rootEntry2, relativePath, loopDetected2, options)]).then( function (entriesResult) { var entries1 = entriesResult[0] var entries2 = entriesResult[1] var i1 = 0, i2 = 0 var comparePromises = [] var compareFilePromises = [] var subDiffSet while (i1 < entries1.length || i2 < entries2.length) { var entry1 = entries1[i1] var entry2 = entries2[i2] var type1, type2 // compare entry name (-1, 0, 1) var cmp if (i1 < entries1.length && i2 < entries2.length) { cmp = entryComparator.compareEntry(entry1, entry2, options) type1 = entryType.getType(entry1) type2 = entryType.getType(entry2) } else if (i1 < entries1.length) { type1 = entryType.getType(entry1) type2 = entryType.getType(undefined) cmp = -1 } else { type1 = entryType.getType(undefined) type2 = entryType.getType(entry2) cmp = 1 } // process entry if (cmp === 0) { // Both left/right exist and have the same name and type var compareAsyncRes = entryEquality.isEntryEqualAsync(entry1, entry2, type1, diffSet, options) var samePromise = compareAsyncRes.samePromise var same = compareAsyncRes.same if (same !== undefined) { options.resultBuilder(entry1, entry2, same ? 'equal' : 'distinct', level, relativePath, options, statistics, diffSet, compareAsyncRes.reason) stats.updateStatisticsBoth(entry1, entry2, compareAsyncRes.same, compareAsyncRes.reason, type1, statistics, options) } else { compareFilePromises.push(samePromise) } i1++ i2++ if (!options.skipSubdirs && type1 === 'directory') { if (!options.noDiffSet) { subDiffSet = [] diffSet.push(subDiffSet) } comparePromises.push(compare(entry1, entry2, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, subDiffSet, loopDetector.cloneSymlinkCache(symlinkCache))) } } else if (cmp < 0) { // Right missing options.resultBuilder(entry1, undefined, 'left', level, relativePath, options, statistics, diffSet) stats.updateStatisticsLeft(entry1, type1, statistics, options) i1++ if (type1 === 'directory' && !options.skipSubdirs) { if (!options.noDiffSet) { subDiffSet = [] diffSet.push(subDiffSet) } comparePromises.push(compare(entry1, undefined, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, subDiffSet, loopDetector.cloneSymlinkCache(symlinkCache))) } } else { // Left missing options.resultBuilder(undefined, entry2, 'right', level, relativePath, options, statistics, diffSet) stats.updateStatisticsRight(entry2, type2, statistics, options) i2++ if (type2 === 'directory' && !options.skipSubdirs) { if (!options.noDiffSet) { subDiffSet = [] diffSet.push(subDiffSet) } comparePromises.push(compare(undefined, entry2, level + 1, pathUtils.join(relativePath, entry2.name), options, statistics, subDiffSet, loopDetector.cloneSymlinkCache(symlinkCache))) } } } return Promise.all(comparePromises).then(function () { return Promise.all(compareFilePromises).then(function (sameResults) { for (var i = 0; i < sameResults.length; i++) { var sameResult = sameResults[i] if (sameResult.error) { return Promise.reject(sameResult.error) } else { options.resultBuilder(sameResult.entry1, sameResult.entry2, sameResult.same ? 'equal' : 'distinct', level, relativePath, options, statistics, sameResult.diffSet, sameResult.reason) stats.updateStatisticsBoth(sameResult.entries1, sameResult.entries2, sameResult.same, sameResult.reason, sameResult.type1, statistics, options) } } }) }) }) } module.exports = compare