summaryrefslogtreecommitdiff
path: root/node_modules/dir-compare/src
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/dir-compare/src')
-rwxr-xr-xnode_modules/dir-compare/src/cli/dircompare.js147
-rw-r--r--node_modules/dir-compare/src/cli/print.js192
-rw-r--r--node_modules/dir-compare/src/compareAsync.js141
-rw-r--r--node_modules/dir-compare/src/compareSync.js90
-rw-r--r--node_modules/dir-compare/src/entry/entryBuilder.js102
-rw-r--r--node_modules/dir-compare/src/entry/entryComparator.js20
-rw-r--r--node_modules/dir-compare/src/entry/entryEquality.js135
-rw-r--r--node_modules/dir-compare/src/entry/entryType.js18
-rw-r--r--node_modules/dir-compare/src/fileCompareHandler/closeFile.js28
-rw-r--r--node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js113
-rw-r--r--node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js196
-rw-r--r--node_modules/dir-compare/src/fs/BufferPool.js46
-rw-r--r--node_modules/dir-compare/src/fs/FileDescriptorQueue.js78
-rw-r--r--node_modules/dir-compare/src/fs/Queue.js63
-rw-r--r--node_modules/dir-compare/src/fs/fsPromise.js26
-rw-r--r--node_modules/dir-compare/src/index.d.ts476
-rw-r--r--node_modules/dir-compare/src/index.js204
-rw-r--r--node_modules/dir-compare/src/nameCompare/defaultNameCompare.js12
-rw-r--r--node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js27
-rw-r--r--node_modules/dir-compare/src/statistics/statisticsLifecycle.js59
-rw-r--r--node_modules/dir-compare/src/statistics/statisticsUpdate.js62
-rw-r--r--node_modules/dir-compare/src/symlink/loopDetector.js51
22 files changed, 2286 insertions, 0 deletions
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 @@
+/// <reference types="node" />
+
+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<Result>
+
+/**
+ * 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<Difference> | 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<Difference>
+}
+
+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<boolean>
+
+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 name1<name2, 1 if name1>name2.
+ */
+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
+}
+