From 703e03aba33f234712206769f57717ba7d92d23d Mon Sep 17 00:00:00 2001 From: LinuxWizard42 Date: Wed, 12 Oct 2022 22:54:37 +0300 Subject: Added export_allowed file to make repository visible in cgit --- node_modules/dir-compare/src/cli/dircompare.js | 147 +++++++ node_modules/dir-compare/src/cli/print.js | 192 +++++++++ node_modules/dir-compare/src/compareAsync.js | 141 ++++++ node_modules/dir-compare/src/compareSync.js | 90 ++++ node_modules/dir-compare/src/entry/entryBuilder.js | 102 +++++ .../dir-compare/src/entry/entryComparator.js | 20 + .../dir-compare/src/entry/entryEquality.js | 135 ++++++ node_modules/dir-compare/src/entry/entryType.js | 18 + .../src/fileCompareHandler/closeFile.js | 28 ++ .../src/fileCompareHandler/defaultFileCompare.js | 113 +++++ .../src/fileCompareHandler/lineBasedFileCompare.js | 196 +++++++++ node_modules/dir-compare/src/fs/BufferPool.js | 46 ++ .../dir-compare/src/fs/FileDescriptorQueue.js | 78 ++++ node_modules/dir-compare/src/fs/Queue.js | 63 +++ node_modules/dir-compare/src/fs/fsPromise.js | 26 ++ node_modules/dir-compare/src/index.d.ts | 476 +++++++++++++++++++++ node_modules/dir-compare/src/index.js | 204 +++++++++ .../src/nameCompare/defaultNameCompare.js | 12 + .../resultBuilder/defaultResultBuilderCallback.js | 27 ++ .../src/statistics/statisticsLifecycle.js | 59 +++ .../dir-compare/src/statistics/statisticsUpdate.js | 62 +++ .../dir-compare/src/symlink/loopDetector.js | 51 +++ 22 files changed, 2286 insertions(+) create mode 100755 node_modules/dir-compare/src/cli/dircompare.js create mode 100644 node_modules/dir-compare/src/cli/print.js create mode 100644 node_modules/dir-compare/src/compareAsync.js create mode 100644 node_modules/dir-compare/src/compareSync.js create mode 100644 node_modules/dir-compare/src/entry/entryBuilder.js create mode 100644 node_modules/dir-compare/src/entry/entryComparator.js create mode 100644 node_modules/dir-compare/src/entry/entryEquality.js create mode 100644 node_modules/dir-compare/src/entry/entryType.js create mode 100644 node_modules/dir-compare/src/fileCompareHandler/closeFile.js create mode 100644 node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js create mode 100644 node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js create mode 100644 node_modules/dir-compare/src/fs/BufferPool.js create mode 100644 node_modules/dir-compare/src/fs/FileDescriptorQueue.js create mode 100644 node_modules/dir-compare/src/fs/Queue.js create mode 100644 node_modules/dir-compare/src/fs/fsPromise.js create mode 100644 node_modules/dir-compare/src/index.d.ts create mode 100644 node_modules/dir-compare/src/index.js create mode 100644 node_modules/dir-compare/src/nameCompare/defaultNameCompare.js create mode 100644 node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js create mode 100644 node_modules/dir-compare/src/statistics/statisticsLifecycle.js create mode 100644 node_modules/dir-compare/src/statistics/statisticsUpdate.js create mode 100644 node_modules/dir-compare/src/symlink/loopDetector.js (limited to 'node_modules/dir-compare/src') diff --git a/node_modules/dir-compare/src/cli/dircompare.js b/node_modules/dir-compare/src/cli/dircompare.js new file mode 100755 index 0000000..593621d --- /dev/null +++ b/node_modules/dir-compare/src/cli/dircompare.js @@ -0,0 +1,147 @@ +#!/usr/bin/env node + +var program = require('commander') +var dircompare = require('../index') +var fs = require('fs') +var util = require('util') +var print = require('./print') +var pjson = require('../../package.json') + +program + .version(pjson.version) + .usage('[options] leftdir rightdir') + .option('-c, --compare-content', 'compare files by content') + .option('-D, --compare-date', 'compare files by date') + .option('--date-tolerance [type]', 'tolerance to be used in date comparison (milliseconds)') + .option('--compare-symlink', 'compare files and directories by symlink') + .option('-f, --filter [type]', 'file name filter', undefined) + .option('-x, --exclude [type]', 'file/directory name exclude filter', undefined) + .option('-S, --skip-subdirs', 'do not recurse into subdirectories') + .option('-L, --skip-symlinks', 'ignore symlinks') + .option('-i, --ignore-case', 'ignores case when comparing file names') + .option('-l, --show-left', 'report - show entries occurring in left dir') + .option('-r, --show-right', 'report - show entries occurring in right dir') + .option('-e, --show-equal', 'report - show identic entries occurring in both dirs') + .option('-d, --show-distinct', 'report - show distinct entries occurring in both dirs') + .option('-a, --show-all', 'report - show all entries') + .option('-w, --whole-report', 'report - include directories in detailed report') + .option('--reason', 'report - show reason when entries are distinct') + .option('--csv', 'report - print details as csv') + .option('--nocolors', 'don\'t use console colors') + .option('--async', 'Make use of multiple cores') + + +program.on('--help', function () { + console.log(' By default files are compared by size.') + console.log(' --date-tolerance defaults to 1000 ms. Two files are considered to have') + console.log(' the same date if the difference between their modification dates fits') + console.log(' within date tolerance.') + console.log() + console.log(' Exit codes:') + console.log(' 0 - entries are identical') + console.log(' 1 - entries are different') + console.log(' 2 - error occurred') + console.log() + console.log(' Examples:') + console.log(' compare by content dircompare -c dir1 dir2') + console.log(' show only different files dircompare -d dir1 dir2') + console.log() + console.log(' exclude filter dircompare -x ".git,node_modules" dir1 dir2') + console.log(' dircompare -x "/tests/expected" dir1 dir2') + console.log(' dircompare -x "**/expected" dir1 dir2') + console.log(' dircompare -x "**/tests/**/*.ts" dir1 dir2') + console.log() + console.log(' include filter dircompare -f "*.js,*.yml" dir1 dir2') + console.log(' dircompare -f "/tests/**/*.js" dir1 dir2') + console.log(' dircompare -f "**/tests/**/*.ts" dir1 dir2') +}) + +// Fix for https://github.com/tj/commander.js/issues/125 +program.allowUnknownOption() +program.parse(process.argv) +var parsed = program.parseOptions(program.normalize(process.argv.slice(2))) +if (parsed.unknown.length > 0) { + console.error('Unknown options: ' + parsed.unknown) + process.exit(2) +} + +var run = function () { + try { + if (program.args.length !== 2) { + program.outputHelp() + process.exit(2) + } else { + var options = {} + + + options.compareContent = program.compareContent + options.compareDate = program.compareDate + options.compareSymlink = program.compareSymlink + options.compareSize = true + options.skipSubdirs = program.skipSubdirs + options.skipSymlinks = program.skipSymlinks + options.ignoreCase = program.ignoreCase + options.includeFilter = program.filter + options.excludeFilter = program.exclude + options.noDiffSet = !(program.showAll || program.showEqual || program.showLeft || program.showRight || program.showDistinct) + options.dateTolerance = program.dateTolerance || 1000 + + var async = program.async + + var path1 = program.args[0] + var path2 = program.args[1] + var abort = false + if (!isNumeric(options.dateTolerance)) { + console.error("Numeric value expected for --date-tolerance") + abort = true + } + if (!fs.existsSync(path1)) { + console.error(util.format("Path '%s' missing"), path1) + abort = true + } + if (!fs.existsSync(path2)) { + console.error(util.format("Path '%s' missing"), path2) + abort = true + } + if (!abort) { + // compare + var comparePromise + if (async) { + comparePromise = dircompare.compare(path1, path2, options) + } else { + comparePromise = new Promise(function (resolve, reject) { + resolve(dircompare.compareSync(path1, path2, options)) + }) + } + + comparePromise.then( + function (res) { + // PRINT DETAILS + print(res, process.stdout, program) + if (res.same) { + process.exit(0) + } else { + process.exit(1) + } + }, + function (error) { + console.error('Error occurred: ' + (error instanceof Error ? error.stack : error)) + process.exit(2) + }) + } else { + process.exit(2) + } + } + } catch (e) { + console.error(e.stack) + process.exit(2) + } +} + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n) +} + + + +run() diff --git a/node_modules/dir-compare/src/cli/print.js b/node_modules/dir-compare/src/cli/print.js new file mode 100644 index 0000000..9861896 --- /dev/null +++ b/node_modules/dir-compare/src/cli/print.js @@ -0,0 +1,192 @@ +var colors = require('colors') +var util = require('util') +var pathUtils = require('path') + +var PATH_SEP = pathUtils.sep + +// Prints dir compare results. +// 'program' represents display options and correspond to dircompare command line parameters. +// Example: 'dircompare --show-all --exclude *.js dir1 dir2' translates into +// program: {showAll: true, exclude: '*.js'} +// +var print = function (res, writer, program) { + var noColor = function (str) { return str } + var colorEqual = program.nocolors ? noColor : colors.green + var colorDistinct = program.nocolors ? noColor : colors.red + var colorLeft = noColor + var colorRight = noColor + var colorDir = noColor + var colorBrokenLinks = noColor + var colorMissing = program.nocolors ? noColor : colors.yellow + + // calculate relative path length for pretty print + var relativePathMaxLength = 0, fileNameMaxLength = 0 + if (!program.csv && res.diffSet) { + res.diffSet.forEach(function (diff) { + if (diff.relativePath.length > relativePathMaxLength) { + relativePathMaxLength = diff.relativePath.length + } + var len = getCompareFile(diff, '??', colorMissing).length + if (len > fileNameMaxLength) { + fileNameMaxLength = len + } + }) + } + + // csv header + if (program.csv) { + writer.write('path,name,state,type,size1,size2,date1,date2,reason\n') + } + if (res.diffSet) { + for (var i = 0; i < res.diffSet.length; i++) { + var detail = res.diffSet[i] + var color, show = true + + if (!program.wholeReport) { + // show only files or broken links + var type = detail.type1 !== 'missing' ? detail.type1 : detail.type2 + if (type !== 'file' && type !== 'broken-link') { + show = false + } + } + if (show) { + switch (detail.state) { + case 'equal': + color = colorEqual + show = program.showAll || program.showEqual ? true : false + break + case 'left': + color = colorLeft + show = program.showAll || program.showLeft ? true : false + break + case 'right': + color = colorRight + show = program.showAll || program.showRight ? true : false + break + case 'distinct': + color = colorDistinct + show = program.showAll || program.showDistinct ? true : false + break + default: + show = true + color = colors.gray + } + if (show) { + if (program.csv) { + printCsv(writer, detail, color) + } else { + printPretty(writer, program, detail, color, colorDir, colorMissing, relativePathMaxLength, fileNameMaxLength) + } + } + } + } + } + + // PRINT STATISTICS + var statTotal, statEqual, statLeft, statRight, statDistinct + if (program.wholeReport) { + statTotal = res.total + statEqual = res.equal + statLeft = res.left + statRight = res.right + statDistinct = res.distinct + } else { + var brokenLInksStats = res.brokenLinks + statTotal = res.totalFiles + brokenLInksStats.totalBrokenLinks + statEqual = res.equalFiles + statLeft = res.leftFiles + brokenLInksStats.leftBrokenLinks + statRight = res.rightFiles + brokenLInksStats.rightBrokenLinks + statDistinct = res.distinctFiles + brokenLInksStats.distinctBrokenLinks + } + if (!program.noDiffIndicator) { + writer.write(res.same ? colorEqual('Entries are identical\n') : colorDistinct('Entries are different\n')) + } + var stats = util.format('total: %s, equal: %s, distinct: %s, only left: %s, only right: %s', + statTotal, + colorEqual(statEqual), + colorDistinct(statDistinct), + colorLeft(statLeft), + colorRight(statRight) + ) + if (res.brokenLinks.totalBrokenLinks > 0) { + stats += util.format(', broken links: %s', colorBrokenLinks(res.brokenLinks.totalBrokenLinks)) + } + stats += '\n' + writer.write(stats) +} + +/** + * Print details for default view mode + */ +var printPretty = function (writer, program, detail, color, dirColor, missingColor, relativePathMaxLength, fileNameMaxLength) { + var path = detail.relativePath === '' ? PATH_SEP : detail.relativePath + + var state + switch (detail.state) { + case 'equal': + state = '==' + break + case 'left': + state = '->' + break + case 'right': + state = '<-' + break + case 'distinct': + state = '<>' + break + default: + state = '?' + } + var type = '' + type = detail.type1 !== 'missing' ? detail.type1 : detail.type2 + if (type === 'directory') { + type = dirColor(type) + } + var cmpEntry = getCompareFile(detail, color(state), missingColor) + var reason = '' + if (program.reason && detail.reason) { + reason = util.format(' <%s>', detail.reason) + } + if (program.wholeReport || type === 'broken-link') { + writer.write(util.format('[%s] %s (%s)%s\n', path, cmpEntry, type, reason)) + } else { + writer.write(util.format('[%s] %s%s\n', path, cmpEntry, reason)) + } +} + +var getCompareFile = function (detail, state, missingcolor) { + p1 = detail.name1 ? detail.name1 : '' + p2 = detail.name2 ? detail.name2 : '' + var missing1 = detail.type1 === 'missing' ? missingcolor('missing') : '' + var missing2 = detail.type2 === 'missing' ? missingcolor('missing') : '' + return util.format('%s%s %s %s%s', missing1, p1, state, missing2, p2) +} + +/** + * Print csv details. + */ +var printCsv = function (writer, detail, color) { + var size1 = '', size2 = '' + if (detail.type1 === 'file') { + size1 = detail.size1 !== undefined ? detail.size1 : '' + } + if (detail.type2 === 'file') { + size2 = detail.size2 !== undefined ? detail.size2 : '' + } + + var date1 = '', date2 = '' + date1 = detail.date1 !== undefined ? detail.date1.toISOString() : '' + date2 = detail.date2 !== undefined ? detail.date2.toISOString() : '' + + var type = '' + type = detail.type1 !== 'missing' ? detail.type1 : detail.type2 + + var path = detail.relativePath ? detail.relativePath : PATH_SEP + var name = (detail.name1 ? detail.name1 : detail.name2) + var reason = detail.reason || '' + + writer.write(util.format('%s,%s,%s,%s,%s,%s,%s,%s,%s\n', path, name, color(detail.state), type, size1, size2, date1, date2, reason)) +} + +module.exports = print diff --git a/node_modules/dir-compare/src/compareAsync.js b/node_modules/dir-compare/src/compareAsync.js new file mode 100644 index 0000000..26a9d68 --- /dev/null +++ b/node_modules/dir-compare/src/compareAsync.js @@ -0,0 +1,141 @@ +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 diff --git a/node_modules/dir-compare/src/compareSync.js b/node_modules/dir-compare/src/compareSync.js new file mode 100644 index 0000000..84ff4b3 --- /dev/null +++ b/node_modules/dir-compare/src/compareSync.js @@ -0,0 +1,90 @@ +var fs = require('fs') +var pathUtils = require('path') +var entryBuilder = require('./entry/entryBuilder') +var entryEquality = require('./entry/entryEquality') +var stats = require('./statistics/statisticsUpdate') +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 [] + } + if (rootEntry.isDirectory) { + var entries = fs.readdirSync(rootEntry.absolutePath) + return entryBuilder.buildDirEntries(rootEntry, entries, relativePath, options) + } + return [rootEntry] +} + +/** + * Compares two directories synchronously. + */ +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) + + var entries1 = getEntries(rootEntry1, relativePath, loopDetected1, options) + var entries2 = getEntries(rootEntry2, relativePath, loopDetected2, options) + var i1 = 0, i2 = 0 + 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 compareEntryRes = entryEquality.isEntryEqualSync(entry1, entry2, type1, options) + options.resultBuilder(entry1, entry2, + compareEntryRes.same ? 'equal' : 'distinct', + level, relativePath, options, statistics, diffSet, + compareEntryRes.reason) + stats.updateStatisticsBoth(entry1, entry2, compareEntryRes.same, compareEntryRes.reason, type1, statistics, options) + i1++ + i2++ + if (!options.skipSubdirs && type1 === 'directory') { + compare(entry1, entry2, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, diffSet, 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) { + compare(entry1, undefined, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, diffSet, 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) { + compare(undefined, entry2, level + 1, pathUtils.join(relativePath, entry2.name), options, statistics, diffSet, loopDetector.cloneSymlinkCache(symlinkCache)) + } + } + } +} + +module.exports = compare diff --git a/node_modules/dir-compare/src/entry/entryBuilder.js b/node_modules/dir-compare/src/entry/entryBuilder.js new file mode 100644 index 0000000..6a46c62 --- /dev/null +++ b/node_modules/dir-compare/src/entry/entryBuilder.js @@ -0,0 +1,102 @@ +var fs = require('fs') +var minimatch = require('minimatch') +var pathUtils = require('path') +var entryComparator = require('./entryComparator') + +var PATH_SEP = pathUtils.sep + +module.exports = { + /** + * Returns the sorted list of entries in a directory. + */ + buildDirEntries: function (rootEntry, dirEntries, relativePath, options) { + var res = [] + for (var i = 0; i < dirEntries.length; i++) { + var entryName = dirEntries[i] + var entryAbsolutePath = rootEntry.absolutePath + PATH_SEP + entryName + var entryPath = rootEntry.path + PATH_SEP + entryName + + var entry = this.buildEntry(entryAbsolutePath, entryPath, entryName) + if (options.skipSymlinks && entry.isSymlink) { + entry.stat = undefined + } + + if (filterEntry(entry, relativePath, options)) { + res.push(entry) + } + } + return res.sort((a, b) => entryComparator.compareEntry(a, b, options)) + }, + + buildEntry: function (absolutePath, path, name) { + var stats = getStatIgnoreBrokenLink(absolutePath) + + return { + name: name, + absolutePath: absolutePath, + path: path, + stat: stats.stat, + lstat: stats.lstat, + isSymlink: stats.lstat.isSymbolicLink(), + isBrokenLink: stats.isBrokenLink, + isDirectory: stats.stat.isDirectory() + } + }, + +} + + +function getStatIgnoreBrokenLink(absolutePath) { + var lstat = fs.lstatSync(absolutePath) + try { + return { + stat: fs.statSync(absolutePath), + lstat: lstat, + isBrokenLink: false + } + } catch (error) { + if (error.code === 'ENOENT') { + return { + stat: lstat, + lstat: lstat, + isBrokenLink: true + } + } + throw error + } +} + +/** + * Filter entries by file name. Returns true if the file is to be processed. + */ +function filterEntry(entry, relativePath, options) { + if (entry.isSymlink && options.skipSymlinks) { + return false + } + var path = pathUtils.join(relativePath, entry.name) + + if ((entry.stat.isFile() && options.includeFilter) && (!match(path, options.includeFilter))) { + return false + } + + if ((options.excludeFilter) && (match(path, options.excludeFilter))) { + return false + } + + return true +} + +/** + * Matches path by pattern. + */ +function match(path, pattern) { + var patternArray = pattern.split(',') + for (var i = 0; i < patternArray.length; i++) { + var pat = patternArray[i] + if (minimatch(path, pat, { dot: true, matchBase: true })) { //nocase + return true + } + } + return false +} + diff --git a/node_modules/dir-compare/src/entry/entryComparator.js b/node_modules/dir-compare/src/entry/entryComparator.js new file mode 100644 index 0000000..d0361f5 --- /dev/null +++ b/node_modules/dir-compare/src/entry/entryComparator.js @@ -0,0 +1,20 @@ +/** + * Determines order criteria for sorting entries in a directory. + */ +module.exports = { + compareEntry: function (a, b, options) { + if (a.isBrokenLink && b.isBrokenLink) { + return options.compareNameHandler(a.name, b.name, options) + } else if (a.isBrokenLink) { + return -1 + } else if (b.isBrokenLink) { + return 1 + } else if (a.stat.isDirectory() && b.stat.isFile()) { + return -1 + } else if (a.stat.isFile() && b.stat.isDirectory()) { + return 1 + } else { + return options.compareNameHandler(a.name, b.name, options) + } + } +} diff --git a/node_modules/dir-compare/src/entry/entryEquality.js b/node_modules/dir-compare/src/entry/entryEquality.js new file mode 100644 index 0000000..f1b8d78 --- /dev/null +++ b/node_modules/dir-compare/src/entry/entryEquality.js @@ -0,0 +1,135 @@ +var fs = require('fs') +/** + * Compares two entries with identical name and type. + */ +module.exports = { + isEntryEqualSync: function (entry1, entry2, type, options) { + if (type === 'file') { + return isFileEqualSync(entry1, entry2, options) + } + if (type === 'directory') { + return isDirectoryEqual(entry1, entry2, options) + } + if (type === 'broken-link') { + return isBrokenLinkEqual() + } + throw new Error('Unexpected type ' + type) + }, + + isEntryEqualAsync: function (entry1, entry2, type, diffSet, options) { + if (type === 'file') { + return isFileEqualAsync(entry1, entry2, type, diffSet, options) + } + if (type === 'directory') { + return isDirectoryEqual(entry1, entry2, options) + } + if (type === 'broken-link') { + return isBrokenLinkEqual() + } + throw new Error('Unexpected type ' + type) + } +} + + +function isFileEqualSync(entry1, entry2, options) { + var p1 = entry1 ? entry1.absolutePath : undefined + var p2 = entry2 ? entry2.absolutePath : undefined + if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) { + return { same: false, reason: 'different-symlink' } + } + if (options.compareSize && entry1.stat.size !== entry2.stat.size) { + return { same: false, reason: 'different-size' } + } + if (options.compareDate && !isDateEqual(entry1.stat.mtime, entry2.stat.mtime, options.dateTolerance)) { + return { same: false, reason: 'different-date' } + } + if (options.compareContent && !options.compareFileSync(p1, entry1.stat, p2, entry2.stat, options)) { + return { same: false, reason: 'different-content' } + } + return { same: true } +} + +function isFileEqualAsync(entry1, entry2, type, diffSet, options) { + var p1 = entry1 ? entry1.absolutePath : undefined + var p2 = entry2 ? entry2.absolutePath : undefined + if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) { + return { same: false, reason: 'different-symlink' } + } + if (options.compareSize && entry1.stat.size !== entry2.stat.size) { + return { same: false, samePromise: undefined, reason: 'different-size' } + } + + if (options.compareDate && !isDateEqual(entry1.stat.mtime, entry2.stat.mtime, options.dateTolerance)) { + return { same: false, samePromise: undefined, reason: 'different-date' } + } + + if (options.compareContent) { + var samePromise = undefined + var subDiffSet + if (!options.noDiffSet) { + subDiffSet = [] + diffSet.push(subDiffSet) + } + samePromise = options.compareFileAsync(p1, entry1.stat, p2, entry2.stat, options) + .then(function (comparisonResult) { + var same, error + if (typeof (comparisonResult) === "boolean") { + same = comparisonResult + } else { + error = comparisonResult + } + + return { + entry1: entry1, entry2: entry2, same: same, + error: error, type1: type, type2: type, + diffSet: subDiffSet, + reason: same ? undefined : 'different-content' + } + }) + .catch(function (error) { + return { + error: error + } + }) + + return { same: undefined, samePromise: samePromise } + } + + return { same: true, samePromise: undefined } +} + +function isDirectoryEqual(entry1, entry2, options) { + if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) { + return { same: false, reason: 'different-symlink' } + } + return { same: true } +} + +function isBrokenLinkEqual() { + return { same: false, reason: 'broken-link' } // broken links are never considered equal +} + +/** + * Compares two dates and returns true/false depending on tolerance (milliseconds). + * Two dates are considered equal if the difference in milliseconds between them is less or equal than tolerance. + */ +function isDateEqual(date1, date2, tolerance) { + return Math.abs(date1.getTime() - date2.getTime()) <= tolerance ? true : false +} + +/** + * Compares two entries for symlink equality. + */ +function isSymlinkEqual(entry1, entry2) { + if (!entry1.isSymlink && !entry2.isSymlink) { + return true + } + if (entry1.isSymlink && entry2.isSymlink && hasIdenticalLink(entry1.absolutePath, entry2.absolutePath)) { + return true + } + return false +} + +function hasIdenticalLink(path1, path2) { + return fs.readlinkSync(path1) === fs.readlinkSync(path2) +} \ No newline at end of file diff --git a/node_modules/dir-compare/src/entry/entryType.js b/node_modules/dir-compare/src/entry/entryType.js new file mode 100644 index 0000000..5dac42a --- /dev/null +++ b/node_modules/dir-compare/src/entry/entryType.js @@ -0,0 +1,18 @@ + +module.exports = { + /** + * One of 'missing','file','directory','broken-link' + */ + getType: function (entry) { + if (!entry) { + return 'missing' + } + if (entry.isBrokenLink) { + return 'broken-link' + } + if (entry.isDirectory) { + return 'directory' + } + return 'file' + } +} \ No newline at end of file diff --git a/node_modules/dir-compare/src/fileCompareHandler/closeFile.js b/node_modules/dir-compare/src/fileCompareHandler/closeFile.js new file mode 100644 index 0000000..10118e1 --- /dev/null +++ b/node_modules/dir-compare/src/fileCompareHandler/closeFile.js @@ -0,0 +1,28 @@ +var fs = require('fs') + +var closeFilesSync = function (fd1, fd2) { + if (fd1) { + fs.closeSync(fd1) + } + if (fd2) { + fs.closeSync(fd2) + } +} + +var closeFilesAsync = function (fd1, fd2, fdQueue) { + if (fd1 && fd2) { + return fdQueue.promises.close(fd1).then(() => fdQueue.promises.close(fd2)) + } + if (fd1) { + return fdQueue.promises.close(fd1) + } + if (fd2) { + return fdQueue.promises.close(fd2) + } +} + + +module.exports = { + closeFilesSync: closeFilesSync, + closeFilesAsync: closeFilesAsync +} diff --git a/node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js b/node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js new file mode 100644 index 0000000..26188ca --- /dev/null +++ b/node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js @@ -0,0 +1,113 @@ +var fs = require('fs') +var bufferEqual = require('buffer-equal') +var FileDescriptorQueue = require('../fs/FileDescriptorQueue') +var closeFilesSync = require('./closeFile').closeFilesSync +var closeFilesAsync = require('./closeFile').closeFilesAsync +var fsPromise = require('../fs/fsPromise') +var BufferPool = require('../fs/BufferPool') + +var MAX_CONCURRENT_FILE_COMPARE = 8 +var BUF_SIZE = 100000 +var fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2) +var bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE); // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently + + +/** + * Compares two partial buffers. + */ +var compareBuffers = function (buf1, buf2, contentSize) { + return bufferEqual(buf1.slice(0, contentSize), buf2.slice(0, contentSize)) +} + +/** + * Compares two files by content. + */ +var compareSync = function (path1, stat1, path2, stat2, options) { + var fd1, fd2 + if (stat1.size !== stat2.size) { + return false + } + var bufferPair = bufferPool.allocateBuffers() + try { + fd1 = fs.openSync(path1, 'r') + fd2 = fs.openSync(path2, 'r') + var buf1 = bufferPair.buf1 + var buf2 = bufferPair.buf2 + var progress = 0 + while (true) { + var size1 = fs.readSync(fd1, buf1, 0, BUF_SIZE, null) + var size2 = fs.readSync(fd2, buf2, 0, BUF_SIZE, null) + if (size1 !== size2) { + return false + } else if (size1 === 0) { + // End of file reached + return true + } else if (!compareBuffers(buf1, buf2, size1)) { + return false + } + } + } finally { + closeFilesSync(fd1, fd2) + bufferPool.freeBuffers(bufferPair) + } +} + + +/** + * Compares two files by content + */ +var compareAsync = function (path1, stat1, path2, stat2, options) { + var fd1, fd2 + var bufferPair + if (stat1.size !== stat2.size) { + return Promise.resolve(false) + } + return Promise.all([fdQueue.promises.open(path1, 'r'), fdQueue.promises.open(path2, 'r')]) + .then(function (fds) { + bufferPair = bufferPool.allocateBuffers() + fd1 = fds[0] + fd2 = fds[1] + var buf1 = bufferPair.buf1 + var buf2 = bufferPair.buf2 + var progress = 0 + var compareAsyncInternal = function () { + return Promise.all([ + fsPromise.read(fd1, buf1, 0, BUF_SIZE, null), + fsPromise.read(fd2, buf2, 0, BUF_SIZE, null) + ]).then(function (bufferSizes) { + var size1 = bufferSizes[0] + var size2 = bufferSizes[1] + if (size1 !== size2) { + return false + } else if (size1 === 0) { + // End of file reached + return true + } else if (!compareBuffers(buf1, buf2, size1)) { + return false + } else { + return compareAsyncInternal() + } + }) + } + return compareAsyncInternal() + }) + .then( + // 'finally' polyfill for node 8 and below + function (res) { + return finalizeAsync(fd1, fd2, bufferPair).then(() => res) + }, + function (err) { + return finalizeAsync(fd1, fd2, bufferPair).then(() => { throw err; }) + } + ) +} + +function finalizeAsync(fd1, fd2, bufferPair) { + bufferPool.freeBuffers(bufferPair) + return closeFilesAsync(fd1, fd2, fdQueue) +} + +module.exports = { + compareSync: compareSync, + compareAsync: compareAsync +} diff --git a/node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js b/node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js new file mode 100644 index 0000000..c2de4b2 --- /dev/null +++ b/node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js @@ -0,0 +1,196 @@ +/** + * Compare files line by line with options to ignore + * line endings and white space differencies. + */ +var fs = require('fs') +var FileDescriptorQueue = require('../fs/FileDescriptorQueue') +var closeFilesSync = require('./closeFile').closeFilesSync +var closeFilesAsync = require('./closeFile').closeFilesAsync +var fsPromise = require('../fs/fsPromise') +var BufferPool = require('../fs/BufferPool') + +const LINE_TOKENIZER_REGEXP = /[^\n]+\n?|\n/g +const TRIM_LINE_ENDING_REGEXP = /\r\n$/g +const SPLIT_CONTENT_AND_LINE_ENDING_REGEXP = /([^\r\n]*)([\r\n]*)/ +const TRIM_WHITE_SPACES_REGEXP = /^[ \f\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+|[ \f\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+$/g + +var MAX_CONCURRENT_FILE_COMPARE = 8 +var BUF_SIZE = 100000 +var fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2) +var bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE); // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently + +function compareSync(path1, stat1, path2, stat2, options) { + var fd1, fd2 + var bufferPair = bufferPool.allocateBuffers() + var bufferSize = options.lineBasedHandlerBufferSize || BUF_SIZE + try { + fd1 = fs.openSync(path1, 'r') + fd2 = fs.openSync(path2, 'r') + var buf1 = bufferPair.buf1 + var buf2 = bufferPair.buf2 + var nextPosition1 = 0, nextPosition2 = 0 + while (true) { + var lines1 = readLinesSync(fd1, buf1, bufferSize, nextPosition1) + var lines2 = readLinesSync(fd2, buf2, bufferSize, nextPosition2) + if (lines1.length === 0 && lines2.length === 0) { + // End of file reached + return true + } + var equalLines = compareLines(lines1, lines2, options) + if (equalLines === 0) { + return false + } + nextPosition1 += calculateSize(lines1, equalLines) + nextPosition2 += calculateSize(lines2, equalLines) + } + } finally { + closeFilesSync(fd1, fd2) + bufferPool.freeBuffers(bufferPair) + } +} + +async function compareAsync(path1, stat1, path2, stat2, options) { + var fd1, fd2 + var bufferSize = options.lineBasedHandlerBufferSize || BUF_SIZE + var bufferPair + try { + var fds = await Promise.all([fdQueue.promises.open(path1, 'r'), fdQueue.promises.open(path2, 'r')]) + bufferPair = bufferPool.allocateBuffers() + fd1 = fds[0] + fd2 = fds[1] + var buf1 = bufferPair.buf1 + var buf2 = bufferPair.buf2 + var nextPosition1 = 0, nextPosition2 = 0 + while (true) { + var lines1 = await readLinesAsync(fd1, buf1, bufferSize, nextPosition1) + var lines2 = await readLinesAsync(fd2, buf2, bufferSize, nextPosition2) + if (lines1.length === 0 && lines2.length === 0) { + // End of file reached + return true + } + var equalLines = compareLines(lines1, lines2, options) + if (equalLines === 0) { + return false + } + nextPosition1 += calculateSize(lines1, equalLines) + nextPosition2 += calculateSize(lines2, equalLines) + } + } finally { + bufferPool.freeBuffers(bufferPair) + await closeFilesAsync(fd1, fd2, fdQueue) + } +} + +/** + * Read lines from file starting with nextPosition. + * Returns 0 lines if eof is reached, otherwise returns at least one complete line. + */ +function readLinesSync(fd, buf, bufferSize, nextPosition) { + var lines = [] + var chunk = "" + while (true) { + var size = fs.readSync(fd, buf, 0, bufferSize, nextPosition) + if (size === 0) { + // end of file + normalizeLastFileLine(lines) + return lines + } + chunk += buf.toString('utf8', 0, size) + lines = chunk.match(LINE_TOKENIZER_REGEXP) + if (lines.length > 1) { + return removeLastIncompleteLine(lines) + } + nextPosition += size + } +} + +/** + * Read lines from file starting with nextPosition. + * Returns 0 lines if eof is reached, otherwise returns at least one complete line. + */ +async function readLinesAsync(fd, buf, bufferSize, nextPosition) { + var lines = [] + var chunk = "" + while (true) { + var size = await fsPromise.read(fd, buf, 0, bufferSize, nextPosition) + if (size === 0) { + // end of file + normalizeLastFileLine(lines) + return lines + } + chunk += buf.toString('utf8', 0, size) + lines = chunk.match(LINE_TOKENIZER_REGEXP) + if (lines.length > 1) { + return removeLastIncompleteLine(lines) + } + nextPosition += size + } +} + +function removeLastIncompleteLine(lines) { + const lastLine = lines[lines.length - 1] + if (!lastLine.endsWith('\n')) { + return lines.slice(0, lines.length - 1) + } + return lines +} + +function normalizeLastFileLine(lines) { + if (lines.length === 0) { + return + } + const lastLine = lines[lines.length - 1] + if (!lastLine.endsWith('\n')) { + lines[lines.length - 1] = lastLine + '\n' + } +} + +function calculateSize(lines, numberOfLines) { + var size = 0 + for (var i = 0; i < numberOfLines; i++) { + var line = lines[i] + size += line.length + } + return size +} + +function compareLines(lines1, lines2, options) { + var equalLines = 0 + var len = lines1.length < lines2.length ? lines1.length : lines2.length + for (var i = 0; i < len; i++) { + var line1 = lines1[i] + var line2 = lines2[i] + if (options.ignoreLineEnding) { + line1 = trimLineEnding(line1) + line2 = trimLineEnding(line2) + } + if (options.ignoreWhiteSpaces) { + line1 = trimSpaces(line1) + line2 = trimSpaces(line2) + } + if (line1 !== line2) { + return equalLines + } + equalLines++ + } + return equalLines +} + +// Trims string like ' abc \n' into 'abc\n' +function trimSpaces(s) { + var matchResult = s.match(SPLIT_CONTENT_AND_LINE_ENDING_REGEXP); + var content = matchResult[1] + var lineEnding = matchResult[2] + var trimmed = content.replace(TRIM_WHITE_SPACES_REGEXP, '') + return trimmed + lineEnding +} + +// Trims string like 'abc\r\n' into 'abc\n' +function trimLineEnding(s) { + return s.replace(TRIM_LINE_ENDING_REGEXP, '\n') +} + +module.exports = { + compareSync: compareSync, + compareAsync: compareAsync +} diff --git a/node_modules/dir-compare/src/fs/BufferPool.js b/node_modules/dir-compare/src/fs/BufferPool.js new file mode 100644 index 0000000..48febce --- /dev/null +++ b/node_modules/dir-compare/src/fs/BufferPool.js @@ -0,0 +1,46 @@ +/** + * Collection of buffers to be shared between async processes. + * Avoids allocating buffers each time async process starts. + * bufSize - size of each buffer + * bufNo - number of buffers + * Caller has to make sure no more than bufNo async processes run simultaneously. + */ +function BufferPool(bufSize, bufNo) { + var bufferPool = [] + for (var i = 0; i < bufNo; i++) { + bufferPool.push({ + buf1: alloc(bufSize), + buf2: alloc(bufSize), + busy: false + }) + } + + var allocateBuffers = function () { + for (var j = 0; j < bufNo; j++) { + var bufferPair = bufferPool[j] + if (!bufferPair.busy) { + bufferPair.busy = true + return bufferPair + } + } + throw new Error('Async buffer limit reached') + } + + return { + allocateBuffers: allocateBuffers, + freeBuffers: freeBuffers + } + + function freeBuffers(bufferPair) { + bufferPair.busy = false + } +} + +function alloc(bufSize) { + if (Buffer.alloc) { + return Buffer.alloc(bufSize) + } + return new Buffer(bufSize) +} + +module.exports = BufferPool diff --git a/node_modules/dir-compare/src/fs/FileDescriptorQueue.js b/node_modules/dir-compare/src/fs/FileDescriptorQueue.js new file mode 100644 index 0000000..f017b20 --- /dev/null +++ b/node_modules/dir-compare/src/fs/FileDescriptorQueue.js @@ -0,0 +1,78 @@ +'use strict' + +var fs = require('fs') +var Queue = require('./Queue') +/** + * Limits the number of concurrent file handlers. + * Use it as a wrapper over fs.open() and fs.close(). + * Example: + * var fdQueue = new FileDescriptorQueue(8) + * fdQueue.open(path, flags, (err, fd) =>{ + * ... + * fdQueue.close(fd, (err) =>{ + * ... + * }) + * }) + * As of node v7, calling fd.close without a callback is deprecated. + */ +var FileDescriptorQueue = function (maxFilesNo) { + var pendingJobs = new Queue() + var activeCount = 0 + + var open = function (path, flags, callback) { + pendingJobs.enqueue({ + path: path, + flags: flags, + callback: callback + }) + process() + } + + var process = function () { + if (pendingJobs.getLength() > 0 && activeCount < maxFilesNo) { + var job = pendingJobs.dequeue() + activeCount++ + fs.open(job.path, job.flags, job.callback) + } + } + + var close = function (fd, callback) { + activeCount-- + fs.close(fd, callback) + process() + } + + var promises = { + open: function (path, flags) { + return new Promise(function (resolve, reject) { + open(path, flags, function (err, fd) { + if (err) { + reject(err) + } else { + resolve(fd) + } + }) + }) + }, + + close: function (fd) { + return new Promise(function (resolve, reject) { + close(fd, function (err) { + if (err) { + reject(err) + } else { + resolve() + } + }) + }) + } + } + + return { + open: open, + close: close, + promises: promises + } +} + +module.exports = FileDescriptorQueue diff --git a/node_modules/dir-compare/src/fs/Queue.js b/node_modules/dir-compare/src/fs/Queue.js new file mode 100644 index 0000000..4d86dd5 --- /dev/null +++ b/node_modules/dir-compare/src/fs/Queue.js @@ -0,0 +1,63 @@ +/* + +Queue.js + +A function to represent a queue + +Created by Kate Morley - http://code.iamkate.com/ - and released under the terms +of the CC0 1.0 Universal legal code: + +http://creativecommons.org/publicdomain/zero/1.0/legalcode + +*/ + +var MAX_UNUSED_ARRAY_SIZE = 10000 + +/* Creates a new queue. A queue is a first-in-first-out (FIFO) data structure - + * items are added to the end of the queue and removed from the front. + */ +function Queue() { + + // initialise the queue and offset + var queue = [] + var offset = 0 + + // Returns the length of the queue. + this.getLength = function () { + return (queue.length - offset) + } + + /* Enqueues the specified item. The parameter is: + * + * item - the item to enqueue + */ + this.enqueue = function (item) { + queue.push(item) + } + + /* Dequeues an item and returns it. If the queue is empty, the value + * 'undefined' is returned. + */ + this.dequeue = function () { + + // if the queue is empty, return immediately + if (queue.length === 0) { + return undefined + } + + // store the item at the front of the queue + var item = queue[offset] + + // increment the offset and remove the free space if necessary + if (++offset > MAX_UNUSED_ARRAY_SIZE) { + queue = queue.slice(offset) + offset = 0 + } + + // return the dequeued item + return item + + } +} + +module.exports = Queue diff --git a/node_modules/dir-compare/src/fs/fsPromise.js b/node_modules/dir-compare/src/fs/fsPromise.js new file mode 100644 index 0000000..f6e3c5a --- /dev/null +++ b/node_modules/dir-compare/src/fs/fsPromise.js @@ -0,0 +1,26 @@ +var fs = require('fs') + +module.exports = { + readdir: function (path) { + return new Promise(function (resolve, reject) { + fs.readdir(path, function (err, files) { + if (err) { + reject(err) + } else { + resolve(files) + } + }) + }) + }, + read: function (fd, buffer, offset, length, position) { + return new Promise(function (resolve, reject) { + fs.read(fd, buffer, offset, length, position, function(err, bytesRead) { + if(err){ + reject(err) + } else { + resolve(bytesRead) + } + }) + }) + }, +} diff --git a/node_modules/dir-compare/src/index.d.ts b/node_modules/dir-compare/src/index.d.ts new file mode 100644 index 0000000..fc84980 --- /dev/null +++ b/node_modules/dir-compare/src/index.d.ts @@ -0,0 +1,476 @@ +/// + +import * as fs from "fs" + +/** + * 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. + */ +export function compareSync(path1: string, path2: string, options?: Options): Result + +/** + * 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. + */ +export function compare(path1: string, path2: string, options?: Options): Promise + +/** + * Comparison options. + */ +export interface Options { + /** + * Properties to be used in various extension points ie. result builder. + */ + [key: string]: any + + /** + * Compares files by size. Defaults to 'false'. + */ + compareSize?: boolean + + /** + * Compares files by date of modification (stat.mtime). Defaults to 'false'. + */ + compareDate?: boolean + + /** + * Two files are considered to have the same date if the difference between their modification dates fits within date tolerance. Defaults to 1000 ms. + */ + dateTolerance?: number + + /** + * Compares files by content. Defaults to 'false'. + */ + compareContent?: boolean + + /** + * Compares entries by symlink. Defaults to 'false'. + */ + compareSymlink?: boolean + + /** + * Skips sub directories. Defaults to 'false'. + */ + skipSubdirs?: boolean + + /** + * Ignore symbolic links. Defaults to 'false'. + */ + skipSymlinks?: boolean + + /** + * Ignores case when comparing names. Defaults to 'false'. + */ + ignoreCase?: boolean + + /** + * Toggles presence of diffSet in output. If true, only statistics are provided. Use this when comparing large number of files to avoid out of memory situations. Defaults to 'false'. + */ + noDiffSet?: boolean + + /** + * File name filter. Comma separated minimatch patterns. See [Glob patterns](https://github.com/gliviu/dir-compare#glob-patterns). + */ + includeFilter?: string + + /** + * File/directory name exclude filter. Comma separated minimatch patterns. See [Glob patterns](https://github.com/gliviu/dir-compare#glob-patterns) + */ + excludeFilter?: string + + /** + * Callback for constructing result. Called for each compared entry pair. + * + * Updates 'statistics' and 'diffSet'. + * + * See [Custom result builder](https://github.com/gliviu/dir-compare#custom-result-builder). + */ + resultBuilder?: ResultBuilder + + /** + * File comparison handler. See [Custom file comparators](https://github.com/gliviu/dir-compare#custom-file-content-comparators). + */ + compareFileSync?: CompareFileSync + + /** + * File comparison handler. See [Custom file comparators](https://github.com/gliviu/dir-compare#custom-file-content-comparators). + */ + compareFileAsync?: CompareFileAsync + + /** + * Entry name comparison handler. See [Custom name comparators](https://github.com/gliviu/dir-compare#custom-name-comparators). + */ + compareNameHandler?: CompareNameHandler +} + +/** + * Callback for constructing result. Called for each compared entry pair. + * + * Updates 'statistics' and 'diffSet'. + */ +export type ResultBuilder = + /** + * @param entry1 Left entry. + * @param entry2 Right entry. + * @param state See [[DifferenceState]]. + * @param level Depth level relative to root dir. + * @param relativePath Path relative to root dir. + * @param statistics Statistics to be updated. + * @param diffSet Status per each entry to be appended. + * Do not append if [[Options.noDiffSet]] is false. + * @param reason See [[Reason]]. Not available if entries are equal. + */ + ( + entry1: Entry | undefined, + entry2: Entry | undefined, + state: DifferenceState, + level: number, + relativePath: string, + options: Options, + statistics: Statistics, + diffSet: Array | undefined, + reason: Reason | undefined + ) => void + +export interface Entry { + name: string + absolutePath: string + path: string + stat: fs.Stats + lstat: fs.Stats + symlink: boolean +} + +/** + * Comparison result. + */ +export interface Result extends Statistics { + /** + * List of changes (present if [[Options.noDiffSet]] is false). + */ + diffSet?: Array +} + +export interface Statistics { + /** + * Any property is allowed if default result builder is not used. + */ + [key: string]: any + + /** + * True if directories are identical. + */ + same: boolean + + /** + * Number of distinct entries. + */ + distinct: number + + /** + * Number of equal entries. + */ + equal: number + + /** + * Number of entries only in path1. + */ + left: number + + /** + * Number of entries only in path2. + */ + right: number + + /** + * Total number of differences (distinct+left+right). + */ + differences: number + + /** + * Total number of entries (differences+equal). + */ + total: number + + /** + * Number of distinct files. + */ + distinctFiles: number + + /** + * Number of equal files. + */ + equalFiles: number + + /** + * Number of files only in path1. + */ + leftFiles: number + + /** + * Number of files only in path2 + */ + rightFiles: number + + /** + * Total number of different files (distinctFiles+leftFiles+rightFiles). + */ + differencesFiles: number + + /** + * Total number of files (differencesFiles+equalFiles). + */ + totalFiles: number + + /** + * Number of distinct directories. + */ + distinctDirs: number + + /** + * Number of equal directories. + */ + equalDirs: number + + /** + * Number of directories only in path1. + */ + leftDirs: number + + /** + * Number of directories only in path2. + */ + rightDirs: number + + /** + * Total number of different directories (distinctDirs+leftDirs+rightDirs). + */ + differencesDirs: number + + /** + * Total number of directories (differencesDirs+equalDirs). + */ + totalDirs: number + + /** + * Stats about broken links. + */ + brokenLinks: BrokenLinksStatistics + + /** + * Statistics available if 'compareSymlink' options is used. + */ + symlinks?: SymlinkStatistics +} + +export interface BrokenLinksStatistics { + /** + * Number of broken links only in path1 + */ + leftBrokenLinks: number + + /** + * Number of broken links only in path2 + */ + rightBrokenLinks: number + + /** + * Number of broken links with same name appearing in both path1 and path2 (leftBrokenLinks+rightBrokenLinks+distinctBrokenLinks) + */ + distinctBrokenLinks: number + + /** + * Total number of broken links + */ + totalBrokenLinks: number + +} + +export interface SymlinkStatistics { + /** + * Number of distinct links. + */ + distinctSymlinks: number + + /** + * Number of equal links. + */ + equalSymlinks: number + + /** + * Number of links only in path1. + */ + leftSymlinks: number + + /** + * Number of links only in path2 + */ + rightSymlinks: number + + /** + * Total number of different links (distinctSymlinks+leftSymlinks+rightSymlinks). + */ + differencesSymlinks: number + + /** + * Total number of links (differencesSymlinks+equalSymlinks). + */ + totalSymlinks: number + +} + +/** + * State of left/right entries relative to each other. + */ +export type DifferenceState = "equal" | "left" | "right" | "distinct" + +/** + * Type of entry. + */ +export type DifferenceType = "missing" | "file" | "directory" | "broken-link" + +/** + * Provides reason when two identically named entries are distinct. + */ +export type Reason = "different-size" | "different-date" | "different-content" | "broken-link" | 'different-symlink' + +export interface Difference { + /** + * Any property is allowed if default result builder is not used. + */ + [key: string]: any + + /** + * Path not including file/directory name; can be relative or absolute depending on call to compare(). + */ + path1?: string + + /** + * Path not including file/directory name; can be relative or absolute depending on call to compare(). + */ + path2?: string + + /** + * Path relative to root dir. + */ + relativePath: string + + /** + * Left file/directory name. + */ + name1?: string + + /** + * Right file/directory name. + */ + name2?: string + + /** + * See [[DifferenceState]] + */ + state: DifferenceState + + /** + * Type of left entry. + */ + type1: DifferenceType + + /** + * Type of right entry. + */ + type2: DifferenceType + + /** + * Left file size. + */ + size1?: number + + /** + * Right file size. + */ + size2?: number + + /** + * Left entry modification date (stat.mtime). + */ + date1?: number + + /** + * Right entry modification date (stat.mtime). + */ + date2?: number + + /** + * Depth level relative to root dir. + */ + level: number + + /** + * See [[Reason]]. + * Not available if entries are equal. + */ + reason?: Reason +} + +/** + * Synchronous file content comparison handler. + */ +export type CompareFileSync = ( + path1: string, + stat1: fs.Stats, + path2: string, + stat2: fs.Stats, + options: Options +) => boolean + +/** + * Asynchronous file content comparison handler. + */ +export type CompareFileAsync = ( + path1: string, + stat1: fs.Stats, + path2: string, + stat2: fs.Stats, + options: Options +) => Promise + +export interface CompareFileHandler { + compareSync: CompareFileSync, + compareAsync: CompareFileAsync +} + +/** + * Available file content comparison handlers. + * These handlers are used when [[Options.compareContent]] is set. + */ +export const fileCompareHandlers: { + /** + * Default file content comparison handlers, used if [[Options.compareFileAsync]] or [[Options.compareFileSync]] are not specified. + * + * Performs binary comparison. + */ + defaultFileCompare: CompareFileHandler, + /** + * Compares files line by line. + * + * Options: + * * ignoreLineEnding - tru/false (default: false) + * * ignoreWhiteSpaces - tru/false (default: false) + */ + lineBasedFileCompare: CompareFileHandler +} + +/** + * Compares the names of two entries. + * The comparison should be dependent on received options (ie. case sensitive, ...). + * Returns 0 if names are identical, -1 if name1name2. + */ +export type CompareNameHandler = ( + name1: string, + name2: string, + options: Options +) => 0 | 1 | -1 diff --git a/node_modules/dir-compare/src/index.js b/node_modules/dir-compare/src/index.js new file mode 100644 index 0000000..fb1b6b8 --- /dev/null +++ b/node_modules/dir-compare/src/index.js @@ -0,0 +1,204 @@ +var util = require('util') +var pathUtils = require('path') +var fs = require('fs') +var compareSyncInternal = require('./compareSync') +var compareAsyncInternal = require('./compareAsync') +var defaultResultBuilderCallback = require('./resultBuilder/defaultResultBuilderCallback') +var defaultFileCompare = require('./fileCompareHandler/defaultFileCompare') +var lineBasedFileCompare = require('./fileCompareHandler/lineBasedFileCompare') +var defaultNameCompare = require('./nameCompare/defaultNameCompare') +var entryBuilder = require('./entry/entryBuilder') +var statsLifecycle = require('./statistics/statisticsLifecycle') +var loopDetector = require('./symlink/loopDetector') + +var ROOT_PATH = pathUtils.sep + +var compareSync = function (path1, path2, options) { + 'use strict' + // realpathSync() is necessary for loop detection to work properly + var absolutePath1 = pathUtils.normalize(pathUtils.resolve(fs.realpathSync(path1))) + var absolutePath2 = pathUtils.normalize(pathUtils.resolve(fs.realpathSync(path2))) + var diffSet + options = prepareOptions(options) + if (!options.noDiffSet) { + diffSet = [] + } + var statistics = statsLifecycle.initStats(options) + compareSyncInternal( + entryBuilder.buildEntry(absolutePath1, path1, pathUtils.basename(absolutePath1)), + entryBuilder.buildEntry(absolutePath2, path2, pathUtils.basename(absolutePath2)), + 0, ROOT_PATH, options, statistics, diffSet, loopDetector.initSymlinkCache()) + statsLifecycle.completeStatistics(statistics, options) + statistics.diffSet = diffSet + + return statistics +} + +var wrapper = { + realPath: function(path, options) { + return new Promise(function (resolve, reject) { + fs.realpath(path, options, function(err, resolvedPath) { + if(err){ + reject(err) + } else { + resolve(resolvedPath) + } + }) + }) + } +} + +var compareAsync = function (path1, path2, options) { + 'use strict' + var absolutePath1, absolutePath2 + return Promise.resolve() + .then(function () { + return Promise.all([wrapper.realPath(path1), wrapper.realPath(path2)]) + }) + .then(function (realPaths) { + var realPath1 = realPaths[0] + var realPath2 = realPaths[1] + // realpath() is necessary for loop detection to work properly + absolutePath1 = pathUtils.normalize(pathUtils.resolve(realPath1)) + absolutePath2 = pathUtils.normalize(pathUtils.resolve(realPath2)) + }) + .then(function () { + options = prepareOptions(options) + var asyncDiffSet + if (!options.noDiffSet) { + asyncDiffSet = [] + } + var statistics = statsLifecycle.initStats(options) + return compareAsyncInternal( + entryBuilder.buildEntry(absolutePath1, path1, pathUtils.basename(path1)), + entryBuilder.buildEntry(absolutePath2, path2, pathUtils.basename(path2)), + 0, ROOT_PATH, options, statistics, asyncDiffSet, loopDetector.initSymlinkCache()).then( + function () { + statsLifecycle.completeStatistics(statistics, options) + if (!options.noDiffSet) { + var diffSet = [] + rebuildAsyncDiffSet(statistics, asyncDiffSet, diffSet) + statistics.diffSet = diffSet + } + return statistics + }) + }) +} + +var prepareOptions = function (options) { + options = options || {} + var clone = JSON.parse(JSON.stringify(options)) + clone.resultBuilder = options.resultBuilder + clone.compareFileSync = options.compareFileSync + clone.compareFileAsync = options.compareFileAsync + clone.compareNameHandler = options.compareNameHandler + if (!clone.resultBuilder) { + clone.resultBuilder = defaultResultBuilderCallback + } + if (!clone.compareFileSync) { + clone.compareFileSync = defaultFileCompare.compareSync + } + if (!clone.compareFileAsync) { + clone.compareFileAsync = defaultFileCompare.compareAsync + } + if(!clone.compareNameHandler) { + clone.compareNameHandler = defaultNameCompare + } + clone.dateTolerance = clone.dateTolerance || 1000 + clone.dateTolerance = Number(clone.dateTolerance) + if (isNaN(clone.dateTolerance)) { + throw new Error('Date tolerance is not a number') + } + return clone +} + + +// Async diffsets are kept into recursive structures. +// This method transforms them into one dimensional arrays. +var rebuildAsyncDiffSet = function (statistics, asyncDiffSet, diffSet) { + asyncDiffSet.forEach(function (rawDiff) { + if (!Array.isArray(rawDiff)) { + diffSet.push(rawDiff) + } else { + rebuildAsyncDiffSet(statistics, rawDiff, diffSet) + } + }) +} + + +/** + * Options: + * compareSize: true/false - Compares files by size. Defaults to 'false'. + * compareDate: true/false - Compares files by date of modification (stat.mtime). Defaults to 'false'. + * dateTolerance: milliseconds - Two files are considered to have the same date if the difference between their modification dates fits within date tolerance. Defaults to 1000 ms. + * compareContent: true/false - Compares files by content. Defaults to 'false'. + * compareSymlink: true/false - Compares entries by symlink. Defaults to 'false'. + * skipSubdirs: true/false - Skips sub directories. Defaults to 'false'. + * skipSymlinks: true/false - Skips symbolic links. Defaults to 'false'. + * ignoreCase: true/false - Ignores case when comparing names. Defaults to 'false'. + * noDiffSet: true/false - Toggles presence of diffSet in output. If true, only statistics are provided. Use this when comparing large number of files to avoid out of memory situations. Defaults to 'false'. + * includeFilter: File name filter. Comma separated [minimatch](https://www.npmjs.com/package/minimatch) patterns. + * excludeFilter: File/directory name exclude filter. Comma separated [minimatch](https://www.npmjs.com/package/minimatch) patterns. + * resultBuilder: Callback for constructing result. + * function (entry1, entry2, state, level, relativePath, options, statistics, diffSet). Called for each compared entry pair. Updates 'statistics' and 'diffSet'. + * compareFileSync, compareFileAsync: Callbacks for file comparison. + * compareNameHandler: Callback for name comparison + * + * Result: + * same: true if directories are identical + * distinct: number of distinct entries + * equal: number of equal entries + * left: number of entries only in path1 + * right: number of entries only in path2 + * differences: total number of differences (distinct+left+right) + * total: total number of entries (differences+equal) + * distinctFiles: number of distinct files + * equalFiles: number of equal files + * leftFiles: number of files only in path1 + * rightFiles: number of files only in path2 + * differencesFiles: total number of different files (distinctFiles+leftFiles+rightFiles) + * totalFiles: total number of files (differencesFiles+equalFiles) + * distinctDirs: number of distinct directories + * equalDirs: number of equal directories + * leftDirs: number of directories only in path1 + * rightDirs: number of directories only in path2 + * differencesDirs: total number of different directories (distinctDirs+leftDirs+rightDirs) + * totalDirs: total number of directories (differencesDirs+equalDirs) + * brokenLinks: + * leftBrokenLinks: number of broken links only in path1 + * rightBrokenLinks: number of broken links only in path2 + * distinctBrokenLinks: number of broken links with same name appearing in both path1 and path2 + * totalBrokenLinks: total number of broken links (leftBrokenLinks+rightBrokenLinks+distinctBrokenLinks) + * symlinks: Statistics available if 'compareSymlink' options is used + * distinctSymlinks: number of distinct links + * equalSymlinks: number of equal links + * leftSymlinks: number of links only in path1 + * rightSymlinks: number of links only in path2 + * differencesSymlinks: total number of different links (distinctSymlinks+leftSymlinks+rightSymlinks) + * totalSymlinks: total number of links (differencesSymlinks+equalSymlinks) + * diffSet - List of changes (present if Options.noDiffSet is false) + * path1: absolute path not including file/directory name, + * path2: absolute path not including file/directory name, + * relativePath: common path relative to root, + * name1: file/directory name + * name2: file/directory name + * state: one of equal, left, right, distinct, + * type1: one of missing, file, directory, broken-link + * type2: one of missing, file, directory, broken-link + * size1: file size + * size2: file size + * date1: modification date (stat.mtime) + * date2: modification date (stat.mtime) + * level: depth + * reason: Provides reason when two identically named entries are distinct + * Not available if entries are equal + * One of "different-size", "different-date", "different-content", "broken-link", "different-symlink" + */ +module.exports = { + compareSync: compareSync, + compare: compareAsync, + fileCompareHandlers: { + defaultFileCompare: defaultFileCompare, + lineBasedFileCompare: lineBasedFileCompare + } +} diff --git a/node_modules/dir-compare/src/nameCompare/defaultNameCompare.js b/node_modules/dir-compare/src/nameCompare/defaultNameCompare.js new file mode 100644 index 0000000..4ed2e99 --- /dev/null +++ b/node_modules/dir-compare/src/nameCompare/defaultNameCompare.js @@ -0,0 +1,12 @@ + +module.exports = function compareName(name1, name2, options) { + if (options.ignoreCase) { + name1 = name1.toLowerCase() + name2 = name2.toLowerCase() + } + return strcmp(name1, name2) +} + +function strcmp(str1, str2) { + return ((str1 === str2) ? 0 : ((str1 > str2) ? 1 : -1)) +} diff --git a/node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js b/node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js new file mode 100644 index 0000000..224b927 --- /dev/null +++ b/node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js @@ -0,0 +1,27 @@ +'use strict' + +var pathUtils = require('path') +var common = require('../entry/entryBuilder') +var entryType = require('../entry/entryType') + +module.exports = function (entry1, entry2, state, level, relativePath, options, statistics, diffSet, reason) { + if (options.noDiffSet) { + return + } + diffSet.push({ + path1: entry1 ? pathUtils.dirname(entry1.path) : undefined, + path2: entry2 ? pathUtils.dirname(entry2.path) : undefined, + relativePath: relativePath, + name1: entry1 ? entry1.name : undefined, + name2: entry2 ? entry2.name : undefined, + state: state, + type1: entryType.getType(entry1), + type2: entryType.getType(entry2), + level: level, + size1: entry1 ? entry1.stat.size : undefined, + size2: entry2 ? entry2.stat.size : undefined, + date1: entry1 ? entry1.stat.mtime : undefined, + date2: entry2 ? entry2.stat.mtime : undefined, + reason: reason + }) +} diff --git a/node_modules/dir-compare/src/statistics/statisticsLifecycle.js b/node_modules/dir-compare/src/statistics/statisticsLifecycle.js new file mode 100644 index 0000000..549ec5e --- /dev/null +++ b/node_modules/dir-compare/src/statistics/statisticsLifecycle.js @@ -0,0 +1,59 @@ +/** + * Controls creation/completion of global statistics object. + */ +module.exports = { + initStats(options) { + var symlinkStatistics = undefined + if (options.compareSymlink) { + symlinkStatistics = { + distinctSymlinks: 0, + equalSymlinks: 0, + leftSymlinks: 0, + rightSymlinks: 0, + differencesSymlinks: 0, + totalSymlinks: 0, + } + } + var brokenLinksStatistics = { + leftBrokenLinks: 0, + rightBrokenLinks: 0, + distinctBrokenLinks: 0, + } + return { + distinct: 0, + equal: 0, + left: 0, + right: 0, + distinctFiles: 0, + equalFiles: 0, + leftFiles: 0, + rightFiles: 0, + distinctDirs: 0, + equalDirs: 0, + leftDirs: 0, + rightDirs: 0, + brokenLinks: brokenLinksStatistics, + symlinks: symlinkStatistics, + same: undefined + } + }, + + completeStatistics(statistics, options) { + statistics.differences = statistics.distinct + statistics.left + statistics.right + statistics.differencesFiles = statistics.distinctFiles + statistics.leftFiles + statistics.rightFiles + statistics.differencesDirs = statistics.distinctDirs + statistics.leftDirs + statistics.rightDirs + statistics.total = statistics.equal + statistics.differences + statistics.totalFiles = statistics.equalFiles + statistics.differencesFiles + statistics.totalDirs = statistics.equalDirs + statistics.differencesDirs + var brokenLInksStats = statistics.brokenLinks + brokenLInksStats.totalBrokenLinks = brokenLInksStats.leftBrokenLinks + brokenLInksStats.rightBrokenLinks + brokenLInksStats.distinctBrokenLinks + statistics.same = statistics.differences ? false : true + + if (options.compareSymlink) { + statistics.symlinks.differencesSymlinks = statistics.symlinks.distinctSymlinks + + statistics.symlinks.leftSymlinks + statistics.symlinks.rightSymlinks + statistics.symlinks.totalSymlinks = statistics.symlinks.differencesSymlinks + statistics.symlinks.equalSymlinks + } + } + +} \ No newline at end of file diff --git a/node_modules/dir-compare/src/statistics/statisticsUpdate.js b/node_modules/dir-compare/src/statistics/statisticsUpdate.js new file mode 100644 index 0000000..a1b2312 --- /dev/null +++ b/node_modules/dir-compare/src/statistics/statisticsUpdate.js @@ -0,0 +1,62 @@ +/** + * Calculates comparison statistics. + */ +module.exports = { + updateStatisticsBoth: function (entry1, entry2, same, reason, type, statistics, options) { + same ? statistics.equal++ : statistics.distinct++ + if (type === 'file') { + same ? statistics.equalFiles++ : statistics.distinctFiles++ + } else if (type === 'directory') { + same ? statistics.equalDirs++ : statistics.distinctDirs++ + } else if (type === 'broken-link') { + statistics.brokenLinks.distinctBrokenLinks++ + } else { + throw new Error('Unexpected type ' + type) + } + + var isSymlink1 = entry1 ? entry1.isSymlink : false + var isSymlink2 = entry2 ? entry2.isSymlink : false + var isSymlink = isSymlink1 || isSymlink2 + if (options.compareSymlink && isSymlink) { + var symlinks = statistics.symlinks + if (reason === 'different-symlink') { + symlinks.distinctSymlinks++ + } else { + symlinks.equalSymlinks++ + } + } + + }, + updateStatisticsLeft: function (entry1, type, statistics, options) { + statistics.left++ + if (type === 'file') { + statistics.leftFiles++ + } else if (type === 'directory') { + statistics.leftDirs++ + } else if (type === 'broken-link') { + statistics.brokenLinks.leftBrokenLinks++ + } else { + throw new Error('Unexpected type ' + type) + } + + if (options.compareSymlink && entry1.isSymlink) { + statistics.symlinks.leftSymlinks++ + } + }, + updateStatisticsRight: function (entry2, type, statistics, options) { + statistics.right++ + if (type === 'file') { + statistics.rightFiles++ + } else if (type === 'directory') { + statistics.rightDirs++ + } else if (type === 'broken-link') { + statistics.brokenLinks.rightBrokenLinks++ + } else { + throw new Error('Unexpected type ' + type) + } + + if (options.compareSymlink && entry2.isSymlink) { + statistics.symlinks.rightSymlinks++ + } + }, +} \ No newline at end of file diff --git a/node_modules/dir-compare/src/symlink/loopDetector.js b/node_modules/dir-compare/src/symlink/loopDetector.js new file mode 100644 index 0000000..0376ebb --- /dev/null +++ b/node_modules/dir-compare/src/symlink/loopDetector.js @@ -0,0 +1,51 @@ +var fs = require('fs') + +/** + * Provides symlink loop detection to directory traversal algorithm. + */ +module.exports = { + detectLoop: function (entry, symlinkCache) { + if (entry && entry.isSymlink) { + var realPath = fs.realpathSync(entry.absolutePath) + if (symlinkCache[realPath]) { + return true + } + } + return false + }, + + initSymlinkCache: function() { + return { + dir1: {}, + dir2: {} + } + }, + + updateSymlinkCache: function(symlinkCache, rootEntry1, rootEntry2, loopDetected1, loopDetected2) { + var symlinkCachePath1, symlinkCachePath2 + if (rootEntry1 && !loopDetected1) { + symlinkCachePath1 = rootEntry1.isSymlink ? fs.realpathSync(rootEntry1.absolutePath) : rootEntry1.absolutePath + symlinkCache.dir1[symlinkCachePath1] = true + } + if (rootEntry2 && !loopDetected2) { + symlinkCachePath2 = rootEntry2.isSymlink ? fs.realpathSync(rootEntry2.absolutePath) : rootEntry2.absolutePath + symlinkCache.dir2[symlinkCachePath2] = true + } + }, + + cloneSymlinkCache: function (symlinkCache) { + return { + dir1: shallowClone(symlinkCache.dir1), + dir2: shallowClone(symlinkCache.dir2) + } + }, +} + +function shallowClone(obj) { + var cloned = {} + Object.keys(obj).forEach(function (key) { + cloned[key] = obj[key] + }) + return cloned +} + -- cgit v1.2.3-86-g962b