forked from olcxjas-softworks/LarpixClient
Update gitignore (sorry)
This commit is contained in:
parent
a8f8c4d7ad
commit
cca8b02fea
6604 changed files with 1219661 additions and 4 deletions
147
electron/node_modules/dir-compare/src/cli/dircompare.js
generated
vendored
Executable file
147
electron/node_modules/dir-compare/src/cli/dircompare.js
generated
vendored
Executable file
|
|
@ -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()
|
||||
192
electron/node_modules/dir-compare/src/cli/print.js
generated
vendored
Normal file
192
electron/node_modules/dir-compare/src/cli/print.js
generated
vendored
Normal file
|
|
@ -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
|
||||
141
electron/node_modules/dir-compare/src/compareAsync.js
generated
vendored
Normal file
141
electron/node_modules/dir-compare/src/compareAsync.js
generated
vendored
Normal file
|
|
@ -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
|
||||
90
electron/node_modules/dir-compare/src/compareSync.js
generated
vendored
Normal file
90
electron/node_modules/dir-compare/src/compareSync.js
generated
vendored
Normal file
|
|
@ -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
|
||||
102
electron/node_modules/dir-compare/src/entry/entryBuilder.js
generated
vendored
Normal file
102
electron/node_modules/dir-compare/src/entry/entryBuilder.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
|
||||
20
electron/node_modules/dir-compare/src/entry/entryComparator.js
generated
vendored
Normal file
20
electron/node_modules/dir-compare/src/entry/entryComparator.js
generated
vendored
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
135
electron/node_modules/dir-compare/src/entry/entryEquality.js
generated
vendored
Normal file
135
electron/node_modules/dir-compare/src/entry/entryEquality.js
generated
vendored
Normal file
|
|
@ -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)
|
||||
}
|
||||
18
electron/node_modules/dir-compare/src/entry/entryType.js
generated
vendored
Normal file
18
electron/node_modules/dir-compare/src/entry/entryType.js
generated
vendored
Normal file
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
28
electron/node_modules/dir-compare/src/fileCompareHandler/closeFile.js
generated
vendored
Normal file
28
electron/node_modules/dir-compare/src/fileCompareHandler/closeFile.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
113
electron/node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js
generated
vendored
Normal file
113
electron/node_modules/dir-compare/src/fileCompareHandler/defaultFileCompare.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
196
electron/node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js
generated
vendored
Normal file
196
electron/node_modules/dir-compare/src/fileCompareHandler/lineBasedFileCompare.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
46
electron/node_modules/dir-compare/src/fs/BufferPool.js
generated
vendored
Normal file
46
electron/node_modules/dir-compare/src/fs/BufferPool.js
generated
vendored
Normal file
|
|
@ -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
|
||||
78
electron/node_modules/dir-compare/src/fs/FileDescriptorQueue.js
generated
vendored
Normal file
78
electron/node_modules/dir-compare/src/fs/FileDescriptorQueue.js
generated
vendored
Normal file
|
|
@ -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
|
||||
63
electron/node_modules/dir-compare/src/fs/Queue.js
generated
vendored
Normal file
63
electron/node_modules/dir-compare/src/fs/Queue.js
generated
vendored
Normal file
|
|
@ -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
|
||||
26
electron/node_modules/dir-compare/src/fs/fsPromise.js
generated
vendored
Normal file
26
electron/node_modules/dir-compare/src/fs/fsPromise.js
generated
vendored
Normal file
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
476
electron/node_modules/dir-compare/src/index.d.ts
generated
vendored
Normal file
476
electron/node_modules/dir-compare/src/index.d.ts
generated
vendored
Normal file
|
|
@ -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
|
||||
204
electron/node_modules/dir-compare/src/index.js
generated
vendored
Normal file
204
electron/node_modules/dir-compare/src/index.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
12
electron/node_modules/dir-compare/src/nameCompare/defaultNameCompare.js
generated
vendored
Normal file
12
electron/node_modules/dir-compare/src/nameCompare/defaultNameCompare.js
generated
vendored
Normal file
|
|
@ -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))
|
||||
}
|
||||
27
electron/node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js
generated
vendored
Normal file
27
electron/node_modules/dir-compare/src/resultBuilder/defaultResultBuilderCallback.js
generated
vendored
Normal file
|
|
@ -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
|
||||
})
|
||||
}
|
||||
59
electron/node_modules/dir-compare/src/statistics/statisticsLifecycle.js
generated
vendored
Normal file
59
electron/node_modules/dir-compare/src/statistics/statisticsLifecycle.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
62
electron/node_modules/dir-compare/src/statistics/statisticsUpdate.js
generated
vendored
Normal file
62
electron/node_modules/dir-compare/src/statistics/statisticsUpdate.js
generated
vendored
Normal file
|
|
@ -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++
|
||||
}
|
||||
},
|
||||
}
|
||||
51
electron/node_modules/dir-compare/src/symlink/loopDetector.js
generated
vendored
Normal file
51
electron/node_modules/dir-compare/src/symlink/loopDetector.js
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue