summaryrefslogtreecommitdiff
path: root/node_modules/dir-compare
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/dir-compare')
-rw-r--r--node_modules/dir-compare/LICENSE22
-rw-r--r--node_modules/dir-compare/README.md374
-rw-r--r--node_modules/dir-compare/node_modules/commander/History.md261
-rw-r--r--node_modules/dir-compare/node_modules/commander/LICENSE22
-rw-r--r--node_modules/dir-compare/node_modules/commander/Readme.md351
-rw-r--r--node_modules/dir-compare/node_modules/commander/index.js1110
-rw-r--r--node_modules/dir-compare/node_modules/commander/package.json33
-rw-r--r--node_modules/dir-compare/node_modules/minimatch/LICENSE15
-rw-r--r--node_modules/dir-compare/node_modules/minimatch/README.md209
-rw-r--r--node_modules/dir-compare/node_modules/minimatch/minimatch.js923
-rw-r--r--node_modules/dir-compare/node_modules/minimatch/package.json30
-rw-r--r--node_modules/dir-compare/package.json59
-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
34 files changed, 5695 insertions, 0 deletions
diff --git a/node_modules/dir-compare/LICENSE b/node_modules/dir-compare/LICENSE
new file mode 100644
index 0000000..50df0a3
--- /dev/null
+++ b/node_modules/dir-compare/LICENSE
@@ -0,0 +1,22 @@
+Copyright 2014 Liviu Grigorescu (grigoresculiviu@gmail.com)
+
+This project is free software released under the MIT license:
+http://www.opensource.org/licenses/mit-license.php
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/node_modules/dir-compare/README.md b/node_modules/dir-compare/README.md
new file mode 100644
index 0000000..ec0b265
--- /dev/null
+++ b/node_modules/dir-compare/README.md
@@ -0,0 +1,374 @@
+dir-compare
+==========
+Node JS directory compare
+
+[![Build Status](https://api.travis-ci.org/gliviu/dir-compare.svg?branch=master)](https://travis-ci.org/gliviu/dir-compare)
+[![Build status](https://ci.appveyor.com/api/projects/status/fpnqkr2gfg7pwkxk/branch/master?svg=true)](https://ci.appveyor.com/project/gliviu/dir-compare)
+[![codecov.io](http://codecov.io/github/gliviu/dir-compare/coverage.svg?branch=master)](http://codecov.io/github/gliviu/dir-compare?branch=master)
+
+- [Installation](#installation)
+- [Library](#library)
+ * [Use](#use)
+ * [Api](#api)
+ * [Glob patterns](#glob-patterns)
+ * [Custom file content comparators](#custom-file-content-comparators)
+ + [Ignore line endings and white spaces](#ignore-line-endings-and-white-spaces)
+ * [Custom name comparators](#custom-name-comparators)
+ * [Custom result builder](#custom-result-builder)
+ * [Symbolic links](#symbolic-links)
+- [Command line](#command-line)
+- [Changelog](#changelog)
+
+# Installation
+```shell
+$ npm install dir-compare
+```
+or
+```shell
+$ npm install -g dir-compare
+```
+for command line utility.
+
+# Library
+
+## Use
+```javascript
+const dircompare = require('dir-compare');
+
+const options = { compareSize: true };
+// Multiple compare strategy can be used simultaneously - compareSize, compareContent, compareDate, compareSymlink.
+// If one comparison fails for a pair of files, they are considered distinct.
+const path1 = '...';
+const path2 = '...';
+
+// Synchronous
+const res = dircompare.compareSync(path1, path2, options)
+print(res)
+
+// Asynchronous
+dircompare.compare(path1, path2, options)
+ .then(res => print(res))
+ .catch(error => console.error(error));
+
+
+function print(result) {
+ console.log('Directories are %s', result.same ? 'identical' : 'different')
+
+ console.log('Statistics - equal entries: %s, distinct entries: %s, left only entries: %s, right only entries: %s, differences: %s',
+ result.equal, result.distinct, result.left, result.right, result.differences)
+
+ result.diffSet.forEach(dif => console.log('Difference - name1: %s, type1: %s, name2: %s, type2: %s, state: %s',
+ dif.name1, dif.type1, dif.name2, dif.type2, dif.state))
+}
+```
+
+Typescript
+```typescript
+import { compare, compareSync, Options, Result } from "dir-compare";
+const path1 = '...';
+const path2 = '...';
+const options: Options = { compareSize: true };
+
+const res: Result = compareSync(path1, path2, options);
+console.log(res)
+
+compare(path1, path2, options)
+ .then(res => console.log(res))
+ .catch(error => console.error(error));
+```
+
+## Api
+
+
+Below is a quick recap of the api. For more details check the [reference documentation](https://gliviu.github.io/dc-api/).
+```typescript
+compare(path1: string, path2: string, options?: Options): Promise<Result>
+compareSync(path1: string, path2: string, options?: Options): Result
+```
+
+```Options```
+* **compareSize**: true/false - Compares files by size. Defaults to 'false'.
+* **compareContent**: true/false - Compares files by content. Defaults to 'false'.
+* **compareFileSync**, **compareFileAsync**: Callbacks for file comparison. See [Custom file content comparators](#custom-file-content-comparators).
+* **compareDate**: true/false - Compares files by date of modification (stat.mtime). Defaults to 'false'.
+* **compareNameHandler**: Callback for name comparison. See [Custom name comparators](#custom-name-comparators).
+* **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.
+* **compareSymlink**: true/false - Compares entries by symlink. Defaults to 'false'.
+* **skipSymlinks**: true/false - Ignore symbolic links. Defaults to 'false'.
+* **skipSubdirs**: true/false - Skips sub directories. 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. See [Glob patterns](#glob-patterns) below.
+* **excludeFilter**: File/directory name exclude filter. Comma separated [minimatch](https://www.npmjs.com/package/minimatch) patterns. See [Glob patterns](#glob-patterns) below.
+* **resultBuilder**: Callback for constructing result. Called for each compared entry pair. Updates `statistics` and `diffSet`. More details in [Custom result builder](#custom-result-builder).
+
+```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**: path not including file/directory name; can be relative or absolute depending on call to compare(),
+ * **path2**: path not including file/directory name; can be relative or absolute depending on call to compare(),
+ * **relativePath**: 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".
+
+## Glob patterns
+[Minimatch](https://www.npmjs.com/package/minimatch) patterns are used to include/exclude files to be compared.
+
+The pattern is matched against the relative path of the entry being compared.
+
+Following examples assume we are comparing two [dir-compare](https://github.com/gliviu/dir-compare) code bases.
+
+
+```
+dircompare -x ".git,node_modules" dir1 dir2') exclude git and node modules directories
+dircompare -x "expected" dir1 dir2') exclude '/tests/expected' directory
+dircompare -x "/tests/expected" dir1 dir2') exclude '/tests/expected' directory
+dircompare -x "**/expected" dir1 dir2') exclude '/tests/expected' directory
+dircompare -x "**/tests/**/*.js" dir1 dir2') exclude all js files in '/tests' directory and subdirectories
+dircompare -f "*.js,*.yml" dir1 dir2') include js and yaml files
+dircompare -f "/tests/**/*.js" dir1 dir2') include all js files in '/tests' directory and subdirectories
+dircompare -f "**/tests/**/*.ts" dir1 dir2') include all js files in '/tests' directory and subdirectories
+```
+
+## Custom file content comparators
+By default file content is binary compared. As of version 1.5.0 custom file comparison handlers may be specified.
+
+Custom handlers are specified by `compareFileSync` and `compareFileAsync` options which correspond to `dircompare.compareSync()` or `dircompare.compare()` methods.
+
+A couple of handlers are included in the library:
+* binary sync compare - `dircompare.fileCompareHandlers.defaultFileCompare.compareSync`
+* binary async compare - `dircompare.fileCompareHandlers.defaultFileCompare.compareAsync`
+* text sync compare - `dircompare.fileCompareHandlers.lineBasedFileCompare.compareSync`
+* text async compare - `dircompare.fileCompareHandlers.lineBasedFileCompare.compareAsync`
+
+Use [defaultFileCompare.js](https://github.com/gliviu/dir-compare/blob/master/src/fileCompareHandler/defaultFileCompare.js) as an example to create your own.
+
+### Ignore line endings and white spaces
+Line based comparator can be used to ignore line ending and white space differences. This comparator is not available in [CLI](#command-line) mode.
+```javascript
+var dircompare = require('dir-compare');
+
+var options = {
+ compareContent: true,
+ compareFileSync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareSync,
+ compareFileAsync: dircompare.fileCompareHandlers.lineBasedFileCompare.compareAsync,
+ ignoreLineEnding: true,
+ ignoreWhiteSpaces: true
+};
+
+var path1 = '...';
+var path2 = '...';
+var res = dircompare.compareSync(path1, path2, options);
+console.log(res)
+
+dircompare.compare(path1, path2, options)
+.then(res => console.log(res))
+```
+## Custom name comparators
+If [default](https://github.com/gliviu/dir-compare/blob/master/src/nameCompare/defaultNameCompare.js) name comparison is not enough, custom behavior can be specified with [compareNameHandler](https://gliviu.github.io/dc-api/index.html#comparenamehandler) option.
+Following example adds the possibility to ignore file extensions.
+```typescript
+import { Options, compare } from 'dir-compare'
+import path from 'path'
+
+var options: Options = {
+ compareSize: false, // compare only name by disabling size and content criteria
+ compareContent: false,
+ compareNameHandler: customNameCompare, // new name comparator used to ignore extensions
+ ignoreExtension: true, // supported by the custom name compare below
+};
+
+function customNameCompare(name1: string, name2: string, options: Options) {
+ if (options.ignoreCase) {
+ name1 = name1.toLowerCase()
+ name2 = name2.toLowerCase()
+ }
+ if (options.ignoreExtension) {
+ name1 = path.basename(name1, path.extname(name1))
+ name2 = path.basename(name2, path.extname(name2))
+ }
+ return ((name1 === name2) ? 0 : ((name1 > name2) ? 1 : -1))
+}
+
+var path1 = '/tmp/a';
+var path2 = '/tmp/b';
+
+var res = compare(path1, path2, options).then(res => {
+ console.log(`Same: ${res.same}`)
+ if (!res.diffSet) {
+ return
+ }
+ res.diffSet.forEach(dif => console.log(`${dif.name1} ${dif.name2} ${dif.state}`))
+})
+
+// Outputs
+// icon.svg icon.png equal
+// logo.svg logo.jpg equal
+```
+
+## Custom result builder
+[Result builder](https://gliviu.github.io/dc-api/index.html#resultbuilder) is called for each pair of entries encountered during comparison. Its purpose is to append entries in `diffSet` and eventually update `statistics` object with new stats.
+
+If needed it can be replaced with custom implementation.
+
+```javascript
+var dircompare = require("dircompare")
+
+var customResultBuilder = function (entry1, entry2, state, level, relativePath, options, statistics, diffSet, reason) {
+ ...
+}
+
+var options = {
+ compareSize: true,
+ resultBuilder: customResultBuilder
+}
+var res = dircompare.compareSync('...', '...', options)
+
+```
+
+The [default](https://github.com/gliviu/dir-compare/blob/master/src/resultBuilder/defaultResultBuilderCallback.js) builder can be used as an example.
+
+## Symbolic links
+Unless `compareSymlink` option is used, symbolic links are resolved and any comparison is applied to the file/directory they point to.
+
+Circular loops are handled by breaking the loop as soon as it is detected.
+
+Version `1.x` treats broken links as `ENOENT: no such file or directory`.
+Since `2.0` they are treated as a special type of entry - `broken-link` - and are available as stats (`totalBrokenLinks`, `distinctBrokenLinks`, ...).
+
+Using `compareSymlink` option causes `dircompare` to check symlink values for equality.
+In this mode two entries with identical name are considered different if
+* one is symlink, the other is not
+* both are symlinks but point to different locations
+
+These rules are applied in addition to the other comparison modes; ie. by content, by size...
+
+If entries are different because of symlinks, `reason` will be `different-symlink`. Also statistics summarizes differences caused by symbolik links.
+
+# Command line
+```
+ Usage: dircompare [options] leftdir rightdir
+
+ Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -c, --compare-content compare files by content
+ -D, --compare-date compare files by date
+ --date-tolerance [type] tolerance to be used in date comparison (milliseconds)
+ --compare-symlink compare files and directories by symlink
+ -f, --filter [type] file name filter
+ -x, --exclude [type] file/directory name exclude filter
+ -S, --skip-subdirs do not recurse into subdirectories
+ -L, --skip-symlinks ignore symlinks
+ -i, --ignore-case ignores case when comparing file names
+ -l, --show-left report - show entries occurring in left dir
+ -r, --show-right report - show entries occurring in right dir
+ -e, --show-equal report - show identic entries occurring in both dirs
+ -d, --show-distinct report - show distinct entries occurring in both dirs
+ -a, --show-all report - show all entries
+ -w, --whole-report report - include directories in detailed report
+ --reason report - show reason when entries are distinct
+ --csv report - print details as csv
+ --nocolors don't use console colors
+ --async Make use of multiple cores
+
+ By default files are compared by size.
+ --date-tolerance defaults to 1000 ms. Two files are considered to have
+ the same date if the difference between their modification dates fits
+ within date tolerance.
+
+ Exit codes:
+ 0 - entries are identical
+ 1 - entries are different
+ 2 - error occurred
+
+ Examples:
+ compare by content dircompare -c dir1 dir2
+ show only different files dircompare -d dir1 dir2
+
+ exclude filter dircompare -x ".git,node_modules" dir1 dir2
+ dircompare -x "/tests/expected" dir1 dir2
+ dircompare -x "**/expected" dir1 dir2
+ dircompare -x "**/tests/**/*.ts" dir1 dir2
+
+ include filter dircompare -f "*.js,*.yml" dir1 dir2
+ dircompare -f "/tests/**/*.js" dir1 dir2
+ dircompare -f "**/tests/**/*.ts" dir1 dir2
+```
+
+# Changelog
+* v2.4.0 New option to customize file/folder name comparison
+* v2.3.0 Fixes
+* v2.1.0 Removed [bluebird](https://github.com/petkaantonov/bluebird/#note) dependency
+* v2.0.0
+ * New option to compare symlinks.
+ * New field indicating reason for two entries being distinct.
+ * Improved command line output format.
+ * Tests are no longer part of published package.
+ * Generated [Api](#api) documentation.
+
+ Breaking changes:
+ * Broken links are no longer treated as errors. As a result there are new statistics (leftBrokenLinks, rightBrokenLinks, distinctBrokenLinks, totalBrokenLinks) and new entry type - broken-link.
+ Details in [Symbolic links](#symbolic-links).
+ * Typescript correction: new interface `Result` replaced `Statistics`.
+* v1.8.0
+ * globstar patterns
+ * typescript corrections
+ * removed support for node 0.11, 0.12, iojs
+* v1.7.0 performance improvements
+* v1.6.0 typescript support
+* v1.5.0 added option to ignore line endings and white space differences
+* v1.3.0 added date tolerance option
+* v1.2.0 added compare by date option
+* v1.1.0
+ * detect symlink loops
+ * improved color scheme for command line utility
+* v1.0.0
+ * asynchronous processing
+ * new library options: noDiffSet, resultBuilder
+ * new statistics: distinctFiles, equalFiles, leftFiles, rightFiles, distinctDirs, equalDirs, leftDirs, rightDirs
+ * new --async command line option
+ * Fix for https://github.com/tj/commander.js/issues/125
+* v0.0.3 Fix fille ordering issue for newer node versions
diff --git a/node_modules/dir-compare/node_modules/commander/History.md b/node_modules/dir-compare/node_modules/commander/History.md
new file mode 100644
index 0000000..1b47439
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/commander/History.md
@@ -0,0 +1,261 @@
+
+2.9.0 / 2015-10-13
+==================
+
+ * Add option `isDefault` to set default subcommand #415 @Qix-
+ * Add callback to allow filtering or post-processing of help text #434 @djulien
+ * Fix `undefined` text in help information close #414 #416 @zhiyelee
+
+2.8.1 / 2015-04-22
+==================
+
+ * Back out `support multiline description` Close #396 #397
+
+2.8.0 / 2015-04-07
+==================
+
+ * Add `process.execArg` support, execution args like `--harmony` will be passed to sub-commands #387 @DigitalIO @zhiyelee
+ * Fix bug in Git-style sub-commands #372 @zhiyelee
+ * Allow commands to be hidden from help #383 @tonylukasavage
+ * When git-style sub-commands are in use, yet none are called, display help #382 @claylo
+ * Add ability to specify arguments syntax for top-level command #258 @rrthomas
+ * Support multiline descriptions #208 @zxqfox
+
+2.7.1 / 2015-03-11
+==================
+
+ * Revert #347 (fix collisions when option and first arg have same name) which causes a bug in #367.
+
+2.7.0 / 2015-03-09
+==================
+
+ * Fix git-style bug when installed globally. Close #335 #349 @zhiyelee
+ * Fix collisions when option and first arg have same name. Close #346 #347 @tonylukasavage
+ * Add support for camelCase on `opts()`. Close #353 @nkzawa
+ * Add node.js 0.12 and io.js to travis.yml
+ * Allow RegEx options. #337 @palanik
+ * Fixes exit code when sub-command failing. Close #260 #332 @pirelenito
+ * git-style `bin` files in $PATH make sense. Close #196 #327 @zhiyelee
+
+2.6.0 / 2014-12-30
+==================
+
+ * added `Command#allowUnknownOption` method. Close #138 #318 @doozr @zhiyelee
+ * Add application description to the help msg. Close #112 @dalssoft
+
+2.5.1 / 2014-12-15
+==================
+
+ * fixed two bugs incurred by variadic arguments. Close #291 @Quentin01 #302 @zhiyelee
+
+2.5.0 / 2014-10-24
+==================
+
+ * add support for variadic arguments. Closes #277 @whitlockjc
+
+2.4.0 / 2014-10-17
+==================
+
+ * fixed a bug on executing the coercion function of subcommands option. Closes #270
+ * added `Command.prototype.name` to retrieve command name. Closes #264 #266 @tonylukasavage
+ * added `Command.prototype.opts` to retrieve all the options as a simple object of key-value pairs. Closes #262 @tonylukasavage
+ * fixed a bug on subcommand name. Closes #248 @jonathandelgado
+ * fixed function normalize doesn’t honor option terminator. Closes #216 @abbr
+
+2.3.0 / 2014-07-16
+==================
+
+ * add command alias'. Closes PR #210
+ * fix: Typos. Closes #99
+ * fix: Unused fs module. Closes #217
+
+2.2.0 / 2014-03-29
+==================
+
+ * add passing of previous option value
+ * fix: support subcommands on windows. Closes #142
+ * Now the defaultValue passed as the second argument of the coercion function.
+
+2.1.0 / 2013-11-21
+==================
+
+ * add: allow cflag style option params, unit test, fixes #174
+
+2.0.0 / 2013-07-18
+==================
+
+ * remove input methods (.prompt, .confirm, etc)
+
+1.3.2 / 2013-07-18
+==================
+
+ * add support for sub-commands to co-exist with the original command
+
+1.3.1 / 2013-07-18
+==================
+
+ * add quick .runningCommand hack so you can opt-out of other logic when running a sub command
+
+1.3.0 / 2013-07-09
+==================
+
+ * add EACCES error handling
+ * fix sub-command --help
+
+1.2.0 / 2013-06-13
+==================
+
+ * allow "-" hyphen as an option argument
+ * support for RegExp coercion
+
+1.1.1 / 2012-11-20
+==================
+
+ * add more sub-command padding
+ * fix .usage() when args are present. Closes #106
+
+1.1.0 / 2012-11-16
+==================
+
+ * add git-style executable subcommand support. Closes #94
+
+1.0.5 / 2012-10-09
+==================
+
+ * fix `--name` clobbering. Closes #92
+ * fix examples/help. Closes #89
+
+1.0.4 / 2012-09-03
+==================
+
+ * add `outputHelp()` method.
+
+1.0.3 / 2012-08-30
+==================
+
+ * remove invalid .version() defaulting
+
+1.0.2 / 2012-08-24
+==================
+
+ * add `--foo=bar` support [arv]
+ * fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]
+
+1.0.1 / 2012-08-03
+==================
+
+ * fix issue #56
+ * fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())
+
+1.0.0 / 2012-07-05
+==================
+
+ * add support for optional option descriptions
+ * add defaulting of `.version()` to package.json's version
+
+0.6.1 / 2012-06-01
+==================
+
+ * Added: append (yes or no) on confirmation
+ * Added: allow node.js v0.7.x
+
+0.6.0 / 2012-04-10
+==================
+
+ * Added `.prompt(obj, callback)` support. Closes #49
+ * Added default support to .choose(). Closes #41
+ * Fixed the choice example
+
+0.5.1 / 2011-12-20
+==================
+
+ * Fixed `password()` for recent nodes. Closes #36
+
+0.5.0 / 2011-12-04
+==================
+
+ * Added sub-command option support [itay]
+
+0.4.3 / 2011-12-04
+==================
+
+ * Fixed custom help ordering. Closes #32
+
+0.4.2 / 2011-11-24
+==================
+
+ * Added travis support
+ * Fixed: line-buffered input automatically trimmed. Closes #31
+
+0.4.1 / 2011-11-18
+==================
+
+ * Removed listening for "close" on --help
+
+0.4.0 / 2011-11-15
+==================
+
+ * Added support for `--`. Closes #24
+
+0.3.3 / 2011-11-14
+==================
+
+ * Fixed: wait for close event when writing help info [Jerry Hamlet]
+
+0.3.2 / 2011-11-01
+==================
+
+ * Fixed long flag definitions with values [felixge]
+
+0.3.1 / 2011-10-31
+==================
+
+ * Changed `--version` short flag to `-V` from `-v`
+ * Changed `.version()` so it's configurable [felixge]
+
+0.3.0 / 2011-10-31
+==================
+
+ * Added support for long flags only. Closes #18
+
+0.2.1 / 2011-10-24
+==================
+
+ * "node": ">= 0.4.x < 0.7.0". Closes #20
+
+0.2.0 / 2011-09-26
+==================
+
+ * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]
+
+0.1.0 / 2011-08-24
+==================
+
+ * Added support for custom `--help` output
+
+0.0.5 / 2011-08-18
+==================
+
+ * Changed: when the user enters nothing prompt for password again
+ * Fixed issue with passwords beginning with numbers [NuckChorris]
+
+0.0.4 / 2011-08-15
+==================
+
+ * Fixed `Commander#args`
+
+0.0.3 / 2011-08-15
+==================
+
+ * Added default option value support
+
+0.0.2 / 2011-08-15
+==================
+
+ * Added mask support to `Command#password(str[, mask], fn)`
+ * Added `Command#password(str, fn)`
+
+0.0.1 / 2010-01-03
+==================
+
+ * Initial release
diff --git a/node_modules/dir-compare/node_modules/commander/LICENSE b/node_modules/dir-compare/node_modules/commander/LICENSE
new file mode 100644
index 0000000..10f997a
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/commander/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/dir-compare/node_modules/commander/Readme.md b/node_modules/dir-compare/node_modules/commander/Readme.md
new file mode 100644
index 0000000..08b9e4c
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/commander/Readme.md
@@ -0,0 +1,351 @@
+# Commander.js
+
+
+[![Build Status](https://api.travis-ci.org/tj/commander.js.svg)](http://travis-ci.org/tj/commander.js)
+[![NPM Version](http://img.shields.io/npm/v/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
+[![NPM Downloads](https://img.shields.io/npm/dm/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
+[![Join the chat at https://gitter.im/tj/commander.js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tj/commander.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+ The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/tj/commander).
+ [API documentation](http://tj.github.com/commander.js/)
+
+
+## Installation
+
+ $ npm install commander
+
+## Option parsing
+
+ Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-p, --peppers', 'Add peppers')
+ .option('-P, --pineapple', 'Add pineapple')
+ .option('-b, --bbq-sauce', 'Add bbq sauce')
+ .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
+ .parse(process.argv);
+
+console.log('you ordered a pizza with:');
+if (program.peppers) console.log(' - peppers');
+if (program.pineapple) console.log(' - pineapple');
+if (program.bbqSauce) console.log(' - bbq');
+console.log(' - %s cheese', program.cheese);
+```
+
+ Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
+
+
+## Coercion
+
+```js
+function range(val) {
+ return val.split('..').map(Number);
+}
+
+function list(val) {
+ return val.split(',');
+}
+
+function collect(val, memo) {
+ memo.push(val);
+ return memo;
+}
+
+function increaseVerbosity(v, total) {
+ return total + 1;
+}
+
+program
+ .version('0.0.1')
+ .usage('[options] <file ...>')
+ .option('-i, --integer <n>', 'An integer argument', parseInt)
+ .option('-f, --float <n>', 'A float argument', parseFloat)
+ .option('-r, --range <a>..<b>', 'A range', range)
+ .option('-l, --list <items>', 'A list', list)
+ .option('-o, --optional [value]', 'An optional value')
+ .option('-c, --collect [value]', 'A repeatable value', collect, [])
+ .option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
+ .parse(process.argv);
+
+console.log(' int: %j', program.integer);
+console.log(' float: %j', program.float);
+console.log(' optional: %j', program.optional);
+program.range = program.range || [];
+console.log(' range: %j..%j', program.range[0], program.range[1]);
+console.log(' list: %j', program.list);
+console.log(' collect: %j', program.collect);
+console.log(' verbosity: %j', program.verbose);
+console.log(' args: %j', program.args);
+```
+
+## Regular Expression
+```js
+program
+ .version('0.0.1')
+ .option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium')
+ .option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i)
+ .parse(process.argv);
+
+console.log(' size: %j', program.size);
+console.log(' drink: %j', program.drink);
+```
+
+## Variadic arguments
+
+ The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to
+ append `...` to the argument name. Here is an example:
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .command('rmdir <dir> [otherDirs...]')
+ .action(function (dir, otherDirs) {
+ console.log('rmdir %s', dir);
+ if (otherDirs) {
+ otherDirs.forEach(function (oDir) {
+ console.log('rmdir %s', oDir);
+ });
+ }
+ });
+
+program.parse(process.argv);
+```
+
+ An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed
+ to your action as demonstrated above.
+
+## Specify the argument syntax
+
+```js
+#!/usr/bin/env node
+
+var program = require('../');
+
+program
+ .version('0.0.1')
+ .arguments('<cmd> [env]')
+ .action(function (cmd, env) {
+ cmdValue = cmd;
+ envValue = env;
+ });
+
+program.parse(process.argv);
+
+if (typeof cmdValue === 'undefined') {
+ console.error('no command given!');
+ process.exit(1);
+}
+console.log('command:', cmdValue);
+console.log('environment:', envValue || "no environment given");
+```
+
+## Git-style sub-commands
+
+```js
+// file: ./examples/pm
+var program = require('..');
+
+program
+ .version('0.0.1')
+ .command('install [name]', 'install one or more packages')
+ .command('search [query]', 'search with optional query')
+ .command('list', 'list packages installed', {isDefault: true})
+ .parse(process.argv);
+```
+
+When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.
+The commander will try to search the executables in the directory of the entry script (like `./examples/pm`) with the name `program-command`, like `pm-install`, `pm-search`.
+
+Options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the option from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified.
+
+If the program is designed to be installed globally, make sure the executables have proper modes, like `755`.
+
+### `--harmony`
+
+You can enable `--harmony` option in two ways:
+* Use `#! /usr/bin/env node --harmony` in the sub-commands scripts. Note some os version don’t support this pattern.
+* Use the `--harmony` option when call the command, like `node --harmony examples/pm publish`. The `--harmony` option will be preserved when spawning sub-command process.
+
+## Automated --help
+
+ The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
+
+```
+ $ ./examples/pizza --help
+
+ Usage: pizza [options]
+
+ An application for pizzas ordering
+
+ Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -p, --peppers Add peppers
+ -P, --pineapple Add pineapple
+ -b, --bbq Add bbq sauce
+ -c, --cheese <type> Add the specified type of cheese [marble]
+ -C, --no-cheese You do not want any cheese
+
+```
+
+## Custom help
+
+ You can display arbitrary `-h, --help` information
+ by listening for "--help". Commander will automatically
+ exit once you are done so that the remainder of your program
+ does not execute causing undesired behaviours, for example
+ in the following executable "stuff" will not output when
+ `--help` is used.
+
+```js
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-f, --foo', 'enable some foo')
+ .option('-b, --bar', 'enable some bar')
+ .option('-B, --baz', 'enable some baz');
+
+// must be before .parse() since
+// node's emit() is immediate
+
+program.on('--help', function(){
+ console.log(' Examples:');
+ console.log('');
+ console.log(' $ custom-help --help');
+ console.log(' $ custom-help -h');
+ console.log('');
+});
+
+program.parse(process.argv);
+
+console.log('stuff');
+```
+
+Yields the following help output when `node script-name.js -h` or `node script-name.js --help` are run:
+
+```
+
+Usage: custom-help [options]
+
+Options:
+
+ -h, --help output usage information
+ -V, --version output the version number
+ -f, --foo enable some foo
+ -b, --bar enable some bar
+ -B, --baz enable some baz
+
+Examples:
+
+ $ custom-help --help
+ $ custom-help -h
+
+```
+
+## .outputHelp(cb)
+
+Output help information without exiting.
+Optional callback cb allows post-processing of help text before it is displayed.
+
+If you want to display help by default (e.g. if no command was provided), you can use something like:
+
+```js
+var program = require('commander');
+var colors = require('colors');
+
+program
+ .version('0.0.1')
+ .command('getstream [url]', 'get stream URL')
+ .parse(process.argv);
+
+ if (!process.argv.slice(2).length) {
+ program.outputHelp(make_red);
+ }
+
+function make_red(txt) {
+ return colors.red(txt); //display the help text in red on the console
+}
+```
+
+## .help(cb)
+
+ Output help information and exit immediately.
+ Optional callback cb allows post-processing of help text before it is displayed.
+
+## Examples
+
+```js
+var program = require('commander');
+
+program
+ .version('0.0.1')
+ .option('-C, --chdir <path>', 'change the working directory')
+ .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ .option('-T, --no-tests', 'ignore test hook')
+
+program
+ .command('setup [env]')
+ .description('run setup commands for all envs')
+ .option("-s, --setup_mode [mode]", "Which setup mode to use")
+ .action(function(env, options){
+ var mode = options.setup_mode || "normal";
+ env = env || 'all';
+ console.log('setup for %s env(s) with %s mode', env, mode);
+ });
+
+program
+ .command('exec <cmd>')
+ .alias('ex')
+ .description('execute the given remote cmd')
+ .option("-e, --exec_mode <mode>", "Which exec mode to use")
+ .action(function(cmd, options){
+ console.log('exec "%s" using %s mode', cmd, options.exec_mode);
+ }).on('--help', function() {
+ console.log(' Examples:');
+ console.log();
+ console.log(' $ deploy exec sequential');
+ console.log(' $ deploy exec async');
+ console.log();
+ });
+
+program
+ .command('*')
+ .action(function(env){
+ console.log('deploying "%s"', env);
+ });
+
+program.parse(process.argv);
+```
+
+More Demos can be found in the [examples](https://github.com/tj/commander.js/tree/master/examples) directory.
+
+## License
+
+MIT
+
diff --git a/node_modules/dir-compare/node_modules/commander/index.js b/node_modules/dir-compare/node_modules/commander/index.js
new file mode 100644
index 0000000..a19c05d
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/commander/index.js
@@ -0,0 +1,1110 @@
+/**
+ * Module dependencies.
+ */
+
+var EventEmitter = require('events').EventEmitter;
+var spawn = require('child_process').spawn;
+var readlink = require('graceful-readlink').readlinkSync;
+var path = require('path');
+var dirname = path.dirname;
+var basename = path.basename;
+var fs = require('fs');
+
+/**
+ * Expose the root command.
+ */
+
+exports = module.exports = new Command();
+
+/**
+ * Expose `Command`.
+ */
+
+exports.Command = Command;
+
+/**
+ * Expose `Option`.
+ */
+
+exports.Option = Option;
+
+/**
+ * Initialize a new `Option` with the given `flags` and `description`.
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @api public
+ */
+
+function Option(flags, description) {
+ this.flags = flags;
+ this.required = ~flags.indexOf('<');
+ this.optional = ~flags.indexOf('[');
+ this.bool = !~flags.indexOf('-no-');
+ flags = flags.split(/[ ,|]+/);
+ if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
+ this.long = flags.shift();
+ this.description = description || '';
+}
+
+/**
+ * Return option name.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Option.prototype.name = function() {
+ return this.long
+ .replace('--', '')
+ .replace('no-', '');
+};
+
+/**
+ * Check if `arg` matches the short or long flag.
+ *
+ * @param {String} arg
+ * @return {Boolean}
+ * @api private
+ */
+
+Option.prototype.is = function(arg) {
+ return arg == this.short || arg == this.long;
+};
+
+/**
+ * Initialize a new `Command`.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+function Command(name) {
+ this.commands = [];
+ this.options = [];
+ this._execs = {};
+ this._allowUnknownOption = false;
+ this._args = [];
+ this._name = name || '';
+}
+
+/**
+ * Inherit from `EventEmitter.prototype`.
+ */
+
+Command.prototype.__proto__ = EventEmitter.prototype;
+
+/**
+ * Add command `name`.
+ *
+ * The `.action()` callback is invoked when the
+ * command `name` is specified via __ARGV__,
+ * and the remaining arguments are applied to the
+ * function for access.
+ *
+ * When the `name` is "*" an un-matched command
+ * will be passed as the first arg, followed by
+ * the rest of __ARGV__ remaining.
+ *
+ * Examples:
+ *
+ * program
+ * .version('0.0.1')
+ * .option('-C, --chdir <path>', 'change the working directory')
+ * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
+ * .option('-T, --no-tests', 'ignore test hook')
+ *
+ * program
+ * .command('setup')
+ * .description('run remote setup commands')
+ * .action(function() {
+ * console.log('setup');
+ * });
+ *
+ * program
+ * .command('exec <cmd>')
+ * .description('run the given remote command')
+ * .action(function(cmd) {
+ * console.log('exec "%s"', cmd);
+ * });
+ *
+ * program
+ * .command('teardown <dir> [otherDirs...]')
+ * .description('run teardown commands')
+ * .action(function(dir, otherDirs) {
+ * console.log('dir "%s"', dir);
+ * if (otherDirs) {
+ * otherDirs.forEach(function (oDir) {
+ * console.log('dir "%s"', oDir);
+ * });
+ * }
+ * });
+ *
+ * program
+ * .command('*')
+ * .description('deploy the given env')
+ * .action(function(env) {
+ * console.log('deploying "%s"', env);
+ * });
+ *
+ * program.parse(process.argv);
+ *
+ * @param {String} name
+ * @param {String} [desc] for git-style sub-commands
+ * @return {Command} the new command
+ * @api public
+ */
+
+Command.prototype.command = function(name, desc, opts) {
+ opts = opts || {};
+ var args = name.split(/ +/);
+ var cmd = new Command(args.shift());
+
+ if (desc) {
+ cmd.description(desc);
+ this.executables = true;
+ this._execs[cmd._name] = true;
+ if (opts.isDefault) this.defaultExecutable = cmd._name;
+ }
+
+ cmd._noHelp = !!opts.noHelp;
+ this.commands.push(cmd);
+ cmd.parseExpectedArgs(args);
+ cmd.parent = this;
+
+ if (desc) return this;
+ return cmd;
+};
+
+/**
+ * Define argument syntax for the top-level command.
+ *
+ * @api public
+ */
+
+Command.prototype.arguments = function (desc) {
+ return this.parseExpectedArgs(desc.split(/ +/));
+};
+
+/**
+ * Add an implicit `help [cmd]` subcommand
+ * which invokes `--help` for the given command.
+ *
+ * @api private
+ */
+
+Command.prototype.addImplicitHelpCommand = function() {
+ this.command('help [cmd]', 'display help for [cmd]');
+};
+
+/**
+ * Parse expected `args`.
+ *
+ * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parseExpectedArgs = function(args) {
+ if (!args.length) return;
+ var self = this;
+ args.forEach(function(arg) {
+ var argDetails = {
+ required: false,
+ name: '',
+ variadic: false
+ };
+
+ switch (arg[0]) {
+ case '<':
+ argDetails.required = true;
+ argDetails.name = arg.slice(1, -1);
+ break;
+ case '[':
+ argDetails.name = arg.slice(1, -1);
+ break;
+ }
+
+ if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
+ argDetails.variadic = true;
+ argDetails.name = argDetails.name.slice(0, -3);
+ }
+ if (argDetails.name) {
+ self._args.push(argDetails);
+ }
+ });
+ return this;
+};
+
+/**
+ * Register callback `fn` for the command.
+ *
+ * Examples:
+ *
+ * program
+ * .command('help')
+ * .description('display verbose help')
+ * .action(function() {
+ * // output help here
+ * });
+ *
+ * @param {Function} fn
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.action = function(fn) {
+ var self = this;
+ var listener = function(args, unknown) {
+ // Parse any so-far unknown options
+ args = args || [];
+ unknown = unknown || [];
+
+ var parsed = self.parseOptions(unknown);
+
+ // Output help if necessary
+ outputHelpIfNecessary(self, parsed.unknown);
+
+ // If there are still any unknown options, then we simply
+ // die, unless someone asked for help, in which case we give it
+ // to them, and then we die.
+ if (parsed.unknown.length > 0) {
+ self.unknownOption(parsed.unknown[0]);
+ }
+
+ // Leftover arguments need to be pushed back. Fixes issue #56
+ if (parsed.args.length) args = parsed.args.concat(args);
+
+ self._args.forEach(function(arg, i) {
+ if (arg.required && null == args[i]) {
+ self.missingArgument(arg.name);
+ } else if (arg.variadic) {
+ if (i !== self._args.length - 1) {
+ self.variadicArgNotLast(arg.name);
+ }
+
+ args[i] = args.splice(i);
+ }
+ });
+
+ // Always append ourselves to the end of the arguments,
+ // to make sure we match the number of arguments the user
+ // expects
+ if (self._args.length) {
+ args[self._args.length] = self;
+ } else {
+ args.push(self);
+ }
+
+ fn.apply(self, args);
+ };
+ var parent = this.parent || this;
+ var name = parent === this ? '*' : this._name;
+ parent.on(name, listener);
+ if (this._alias) parent.on(this._alias, listener);
+ return this;
+};
+
+/**
+ * Define option with `flags`, `description` and optional
+ * coercion `fn`.
+ *
+ * The `flags` string should contain both the short and long flags,
+ * separated by comma, a pipe or space. The following are all valid
+ * all will output this way when `--help` is used.
+ *
+ * "-p, --pepper"
+ * "-p|--pepper"
+ * "-p --pepper"
+ *
+ * Examples:
+ *
+ * // simple boolean defaulting to false
+ * program.option('-p, --pepper', 'add pepper');
+ *
+ * --pepper
+ * program.pepper
+ * // => Boolean
+ *
+ * // simple boolean defaulting to true
+ * program.option('-C, --no-cheese', 'remove cheese');
+ *
+ * program.cheese
+ * // => true
+ *
+ * --no-cheese
+ * program.cheese
+ * // => false
+ *
+ * // required argument
+ * program.option('-C, --chdir <path>', 'change the working directory');
+ *
+ * --chdir /tmp
+ * program.chdir
+ * // => "/tmp"
+ *
+ * // optional argument
+ * program.option('-c, --cheese [type]', 'add cheese [marble]');
+ *
+ * @param {String} flags
+ * @param {String} description
+ * @param {Function|Mixed} fn or default
+ * @param {Mixed} defaultValue
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.option = function(flags, description, fn, defaultValue) {
+ var self = this
+ , option = new Option(flags, description)
+ , oname = option.name()
+ , name = camelcase(oname);
+
+ // default as 3rd arg
+ if (typeof fn != 'function') {
+ if (fn instanceof RegExp) {
+ var regex = fn;
+ fn = function(val, def) {
+ var m = regex.exec(val);
+ return m ? m[0] : def;
+ }
+ }
+ else {
+ defaultValue = fn;
+ fn = null;
+ }
+ }
+
+ // preassign default value only for --no-*, [optional], or <required>
+ if (false == option.bool || option.optional || option.required) {
+ // when --no-* we make sure default is true
+ if (false == option.bool) defaultValue = true;
+ // preassign only if we have a default
+ if (undefined !== defaultValue) self[name] = defaultValue;
+ }
+
+ // register the option
+ this.options.push(option);
+
+ // when it's passed assign the value
+ // and conditionally invoke the callback
+ this.on(oname, function(val) {
+ // coercion
+ if (null !== val && fn) val = fn(val, undefined === self[name]
+ ? defaultValue
+ : self[name]);
+
+ // unassigned or bool
+ if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
+ // if no value, bool true, and we have a default, then use it!
+ if (null == val) {
+ self[name] = option.bool
+ ? defaultValue || true
+ : false;
+ } else {
+ self[name] = val;
+ }
+ } else if (null !== val) {
+ // reassign
+ self[name] = val;
+ }
+ });
+
+ return this;
+};
+
+/**
+ * Allow unknown options on the command line.
+ *
+ * @param {Boolean} arg if `true` or omitted, no error will be thrown
+ * for unknown options.
+ * @api public
+ */
+Command.prototype.allowUnknownOption = function(arg) {
+ this._allowUnknownOption = arguments.length === 0 || arg;
+ return this;
+};
+
+/**
+ * Parse `argv`, settings options and invoking commands when defined.
+ *
+ * @param {Array} argv
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.parse = function(argv) {
+ // implicit help
+ if (this.executables) this.addImplicitHelpCommand();
+
+ // store raw args
+ this.rawArgs = argv;
+
+ // guess name
+ this._name = this._name || basename(argv[1], '.js');
+
+ // github-style sub-commands with no sub-command
+ if (this.executables && argv.length < 3 && !this.defaultExecutable) {
+ // this user needs help
+ argv.push('--help');
+ }
+
+ // process argv
+ var parsed = this.parseOptions(this.normalize(argv.slice(2)));
+ var args = this.args = parsed.args;
+
+ var result = this.parseArgs(this.args, parsed.unknown);
+
+ // executable sub-commands
+ var name = result.args[0];
+ if (this._execs[name] && typeof this._execs[name] != "function") {
+ return this.executeSubCommand(argv, args, parsed.unknown);
+ } else if (this.defaultExecutable) {
+ // use the default subcommand
+ args.unshift(name = this.defaultExecutable);
+ return this.executeSubCommand(argv, args, parsed.unknown);
+ }
+
+ return result;
+};
+
+/**
+ * Execute a sub-command executable.
+ *
+ * @param {Array} argv
+ * @param {Array} args
+ * @param {Array} unknown
+ * @api private
+ */
+
+Command.prototype.executeSubCommand = function(argv, args, unknown) {
+ args = args.concat(unknown);
+
+ if (!args.length) this.help();
+ if ('help' == args[0] && 1 == args.length) this.help();
+
+ // <cmd> --help
+ if ('help' == args[0]) {
+ args[0] = args[1];
+ args[1] = '--help';
+ }
+
+ // executable
+ var f = argv[1];
+ // name of the subcommand, link `pm-install`
+ var bin = basename(f, '.js') + '-' + args[0];
+
+
+ // In case of globally installed, get the base dir where executable
+ // subcommand file should be located at
+ var baseDir
+ , link = readlink(f);
+
+ // when symbolink is relative path
+ if (link !== f && link.charAt(0) !== '/') {
+ link = path.join(dirname(f), link)
+ }
+ baseDir = dirname(link);
+
+ // prefer local `./<bin>` to bin in the $PATH
+ var localBin = path.join(baseDir, bin);
+
+ // whether bin file is a js script with explicit `.js` extension
+ var isExplicitJS = false;
+ if (exists(localBin + '.js')) {
+ bin = localBin + '.js';
+ isExplicitJS = true;
+ } else if (exists(localBin)) {
+ bin = localBin;
+ }
+
+ args = args.slice(1);
+
+ var proc;
+ if (process.platform !== 'win32') {
+ if (isExplicitJS) {
+ args.unshift(localBin);
+ // add executable arguments to spawn
+ args = (process.execArgv || []).concat(args);
+
+ proc = spawn('node', args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ } else {
+ proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] });
+ }
+ } else {
+ args.unshift(localBin);
+ proc = spawn(process.execPath, args, { stdio: 'inherit'});
+ }
+
+ proc.on('close', process.exit.bind(process));
+ proc.on('error', function(err) {
+ if (err.code == "ENOENT") {
+ console.error('\n %s(1) does not exist, try --help\n', bin);
+ } else if (err.code == "EACCES") {
+ console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
+ }
+ process.exit(1);
+ });
+
+ // Store the reference to the child process
+ this.runningCommand = proc;
+};
+
+/**
+ * Normalize `args`, splitting joined short flags. For example
+ * the arg "-abc" is equivalent to "-a -b -c".
+ * This also normalizes equal sign and splits "--abc=def" into "--abc def".
+ *
+ * @param {Array} args
+ * @return {Array}
+ * @api private
+ */
+
+Command.prototype.normalize = function(args) {
+ var ret = []
+ , arg
+ , lastOpt
+ , index;
+
+ for (var i = 0, len = args.length; i < len; ++i) {
+ arg = args[i];
+ if (i > 0) {
+ lastOpt = this.optionFor(args[i-1]);
+ }
+
+ if (arg === '--') {
+ // Honor option terminator
+ ret = ret.concat(args.slice(i));
+ break;
+ } else if (lastOpt && lastOpt.required) {
+ ret.push(arg);
+ } else if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
+ arg.slice(1).split('').forEach(function(c) {
+ ret.push('-' + c);
+ });
+ } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
+ ret.push(arg.slice(0, index), arg.slice(index + 1));
+ } else {
+ ret.push(arg);
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Parse command `args`.
+ *
+ * When listener(s) are available those
+ * callbacks are invoked, otherwise the "*"
+ * event is emitted and those actions are invoked.
+ *
+ * @param {Array} args
+ * @return {Command} for chaining
+ * @api private
+ */
+
+Command.prototype.parseArgs = function(args, unknown) {
+ var name;
+
+ if (args.length) {
+ name = args[0];
+ if (this.listeners(name).length) {
+ this.emit(args.shift(), args, unknown);
+ } else {
+ this.emit('*', args);
+ }
+ } else {
+ outputHelpIfNecessary(this, unknown);
+
+ // If there were no args and we have unknown options,
+ // then they are extraneous and we need to error.
+ if (unknown.length > 0) {
+ this.unknownOption(unknown[0]);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Return an option matching `arg` if any.
+ *
+ * @param {String} arg
+ * @return {Option}
+ * @api private
+ */
+
+Command.prototype.optionFor = function(arg) {
+ for (var i = 0, len = this.options.length; i < len; ++i) {
+ if (this.options[i].is(arg)) {
+ return this.options[i];
+ }
+ }
+};
+
+/**
+ * Parse options from `argv` returning `argv`
+ * void of these options.
+ *
+ * @param {Array} argv
+ * @return {Array}
+ * @api public
+ */
+
+Command.prototype.parseOptions = function(argv) {
+ var args = []
+ , len = argv.length
+ , literal
+ , option
+ , arg;
+
+ var unknownOptions = [];
+
+ // parse options
+ for (var i = 0; i < len; ++i) {
+ arg = argv[i];
+
+ // literal args after --
+ if ('--' == arg) {
+ literal = true;
+ continue;
+ }
+
+ if (literal) {
+ args.push(arg);
+ continue;
+ }
+
+ // find matching Option
+ option = this.optionFor(arg);
+
+ // option is defined
+ if (option) {
+ // requires arg
+ if (option.required) {
+ arg = argv[++i];
+ if (null == arg) return this.optionMissingArgument(option);
+ this.emit(option.name(), arg);
+ // optional arg
+ } else if (option.optional) {
+ arg = argv[i+1];
+ if (null == arg || ('-' == arg[0] && '-' != arg)) {
+ arg = null;
+ } else {
+ ++i;
+ }
+ this.emit(option.name(), arg);
+ // bool
+ } else {
+ this.emit(option.name());
+ }
+ continue;
+ }
+
+ // looks like an option
+ if (arg.length > 1 && '-' == arg[0]) {
+ unknownOptions.push(arg);
+
+ // If the next argument looks like it might be
+ // an argument for this option, we pass it on.
+ // If it isn't, then it'll simply be ignored
+ if (argv[i+1] && '-' != argv[i+1][0]) {
+ unknownOptions.push(argv[++i]);
+ }
+ continue;
+ }
+
+ // arg
+ args.push(arg);
+ }
+
+ return { args: args, unknown: unknownOptions };
+};
+
+/**
+ * Return an object containing options as key-value pairs
+ *
+ * @return {Object}
+ * @api public
+ */
+Command.prototype.opts = function() {
+ var result = {}
+ , len = this.options.length;
+
+ for (var i = 0 ; i < len; i++) {
+ var key = camelcase(this.options[i].name());
+ result[key] = key === 'version' ? this._version : this[key];
+ }
+ return result;
+};
+
+/**
+ * Argument `name` is missing.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.missingArgument = function(name) {
+ console.error();
+ console.error(" error: missing required argument `%s'", name);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * `Option` is missing an argument, but received `flag` or nothing.
+ *
+ * @param {String} option
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.optionMissingArgument = function(option, flag) {
+ console.error();
+ if (flag) {
+ console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
+ } else {
+ console.error(" error: option `%s' argument missing", option.flags);
+ }
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Unknown option `flag`.
+ *
+ * @param {String} flag
+ * @api private
+ */
+
+Command.prototype.unknownOption = function(flag) {
+ if (this._allowUnknownOption) return;
+ console.error();
+ console.error(" error: unknown option `%s'", flag);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Variadic argument with `name` is not the last argument as required.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+Command.prototype.variadicArgNotLast = function(name) {
+ console.error();
+ console.error(" error: variadic arguments must be last `%s'", name);
+ console.error();
+ process.exit(1);
+};
+
+/**
+ * Set the program version to `str`.
+ *
+ * This method auto-registers the "-V, --version" flag
+ * which will print the version number when passed.
+ *
+ * @param {String} str
+ * @param {String} flags
+ * @return {Command} for chaining
+ * @api public
+ */
+
+Command.prototype.version = function(str, flags) {
+ if (0 == arguments.length) return this._version;
+ this._version = str;
+ flags = flags || '-V, --version';
+ this.option(flags, 'output the version number');
+ this.on('version', function() {
+ process.stdout.write(str + '\n');
+ process.exit(0);
+ });
+ return this;
+};
+
+/**
+ * Set the description to `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.description = function(str) {
+ if (0 === arguments.length) return this._description;
+ this._description = str;
+ return this;
+};
+
+/**
+ * Set an alias for the command
+ *
+ * @param {String} alias
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.alias = function(alias) {
+ if (0 == arguments.length) return this._alias;
+ this._alias = alias;
+ return this;
+};
+
+/**
+ * Set / get the command usage `str`.
+ *
+ * @param {String} str
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.usage = function(str) {
+ var args = this._args.map(function(arg) {
+ return humanReadableArgName(arg);
+ });
+
+ var usage = '[options]'
+ + (this.commands.length ? ' [command]' : '')
+ + (this._args.length ? ' ' + args.join(' ') : '');
+
+ if (0 == arguments.length) return this._usage || usage;
+ this._usage = str;
+
+ return this;
+};
+
+/**
+ * Get the name of the command
+ *
+ * @param {String} name
+ * @return {String|Command}
+ * @api public
+ */
+
+Command.prototype.name = function() {
+ return this._name;
+};
+
+/**
+ * Return the largest option length.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+Command.prototype.largestOptionLength = function() {
+ return this.options.reduce(function(max, option) {
+ return Math.max(max, option.flags.length);
+ }, 0);
+};
+
+/**
+ * Return help for options.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.optionHelp = function() {
+ var width = this.largestOptionLength();
+
+ // Prepend the help information
+ return [pad('-h, --help', width) + ' ' + 'output usage information']
+ .concat(this.options.map(function(option) {
+ return pad(option.flags, width) + ' ' + option.description;
+ }))
+ .join('\n');
+};
+
+/**
+ * Return command help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.commandHelp = function() {
+ if (!this.commands.length) return '';
+
+ var commands = this.commands.filter(function(cmd) {
+ return !cmd._noHelp;
+ }).map(function(cmd) {
+ var args = cmd._args.map(function(arg) {
+ return humanReadableArgName(arg);
+ }).join(' ');
+
+ return [
+ cmd._name
+ + (cmd._alias ? '|' + cmd._alias : '')
+ + (cmd.options.length ? ' [options]' : '')
+ + ' ' + args
+ , cmd.description()
+ ];
+ });
+
+ var width = commands.reduce(function(max, command) {
+ return Math.max(max, command[0].length);
+ }, 0);
+
+ return [
+ ''
+ , ' Commands:'
+ , ''
+ , commands.map(function(cmd) {
+ var desc = cmd[1] ? ' ' + cmd[1] : '';
+ return pad(cmd[0], width) + desc;
+ }).join('\n').replace(/^/gm, ' ')
+ , ''
+ ].join('\n');
+};
+
+/**
+ * Return program help documentation.
+ *
+ * @return {String}
+ * @api private
+ */
+
+Command.prototype.helpInformation = function() {
+ var desc = [];
+ if (this._description) {
+ desc = [
+ ' ' + this._description
+ , ''
+ ];
+ }
+
+ var cmdName = this._name;
+ if (this._alias) {
+ cmdName = cmdName + '|' + this._alias;
+ }
+ var usage = [
+ ''
+ ,' Usage: ' + cmdName + ' ' + this.usage()
+ , ''
+ ];
+
+ var cmds = [];
+ var commandHelp = this.commandHelp();
+ if (commandHelp) cmds = [commandHelp];
+
+ var options = [
+ ' Options:'
+ , ''
+ , '' + this.optionHelp().replace(/^/gm, ' ')
+ , ''
+ , ''
+ ];
+
+ return usage
+ .concat(cmds)
+ .concat(desc)
+ .concat(options)
+ .join('\n');
+};
+
+/**
+ * Output help information for this command
+ *
+ * @api public
+ */
+
+Command.prototype.outputHelp = function(cb) {
+ if (!cb) {
+ cb = function(passthru) {
+ return passthru;
+ }
+ }
+ process.stdout.write(cb(this.helpInformation()));
+ this.emit('--help');
+};
+
+/**
+ * Output help information and exit.
+ *
+ * @api public
+ */
+
+Command.prototype.help = function(cb) {
+ this.outputHelp(cb);
+ process.exit();
+};
+
+/**
+ * Camel-case the given `flag`
+ *
+ * @param {String} flag
+ * @return {String}
+ * @api private
+ */
+
+function camelcase(flag) {
+ return flag.split('-').reduce(function(str, word) {
+ return str + word[0].toUpperCase() + word.slice(1);
+ });
+}
+
+/**
+ * Pad `str` to `width`.
+ *
+ * @param {String} str
+ * @param {Number} width
+ * @return {String}
+ * @api private
+ */
+
+function pad(str, width) {
+ var len = Math.max(0, width - str.length);
+ return str + Array(len + 1).join(' ');
+}
+
+/**
+ * Output help information if necessary
+ *
+ * @param {Command} command to output help for
+ * @param {Array} array of options to search for -h or --help
+ * @api private
+ */
+
+function outputHelpIfNecessary(cmd, options) {
+ options = options || [];
+ for (var i = 0; i < options.length; i++) {
+ if (options[i] == '--help' || options[i] == '-h') {
+ cmd.outputHelp();
+ process.exit(0);
+ }
+ }
+}
+
+/**
+ * Takes an argument an returns its human readable equivalent for help usage.
+ *
+ * @param {Object} arg
+ * @return {String}
+ * @api private
+ */
+
+function humanReadableArgName(arg) {
+ var nameOutput = arg.name + (arg.variadic === true ? '...' : '');
+
+ return arg.required
+ ? '<' + nameOutput + '>'
+ : '[' + nameOutput + ']'
+}
+
+// for versions before node v0.8 when there weren't `fs.existsSync`
+function exists(file) {
+ try {
+ if (fs.statSync(file).isFile()) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+}
+
diff --git a/node_modules/dir-compare/node_modules/commander/package.json b/node_modules/dir-compare/node_modules/commander/package.json
new file mode 100644
index 0000000..7731149
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/commander/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "commander",
+ "version": "2.9.0",
+ "description": "the complete solution for node.js command-line programs",
+ "keywords": [
+ "command",
+ "option",
+ "parser"
+ ],
+ "author": "TJ Holowaychuk <tj@vision-media.ca>",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/tj/commander.js.git"
+ },
+ "devDependencies": {
+ "should": ">= 0.0.1",
+ "sinon": ">=1.17.1"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "main": "index",
+ "engines": {
+ "node": ">= 0.6.x"
+ },
+ "files": [
+ "index.js"
+ ],
+ "dependencies": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+}
diff --git a/node_modules/dir-compare/node_modules/minimatch/LICENSE b/node_modules/dir-compare/node_modules/minimatch/LICENSE
new file mode 100644
index 0000000..19129e3
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/minimatch/LICENSE
@@ -0,0 +1,15 @@
+The ISC License
+
+Copyright (c) Isaac Z. Schlueter and Contributors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/node_modules/dir-compare/node_modules/minimatch/README.md b/node_modules/dir-compare/node_modules/minimatch/README.md
new file mode 100644
index 0000000..ad72b81
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/minimatch/README.md
@@ -0,0 +1,209 @@
+# minimatch
+
+A minimal matching utility.
+
+[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.svg)](http://travis-ci.org/isaacs/minimatch)
+
+
+This is the matching library used internally by npm.
+
+It works by converting glob expressions into JavaScript `RegExp`
+objects.
+
+## Usage
+
+```javascript
+var minimatch = require("minimatch")
+
+minimatch("bar.foo", "*.foo") // true!
+minimatch("bar.foo", "*.bar") // false!
+minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
+```
+
+## Features
+
+Supports these glob features:
+
+* Brace Expansion
+* Extended glob matching
+* "Globstar" `**` matching
+
+See:
+
+* `man sh`
+* `man bash`
+* `man 3 fnmatch`
+* `man 5 gitignore`
+
+## Minimatch Class
+
+Create a minimatch object by instantiating the `minimatch.Minimatch` class.
+
+```javascript
+var Minimatch = require("minimatch").Minimatch
+var mm = new Minimatch(pattern, options)
+```
+
+### Properties
+
+* `pattern` The original pattern the minimatch object represents.
+* `options` The options supplied to the constructor.
+* `set` A 2-dimensional array of regexp or string expressions.
+ Each row in the
+ array corresponds to a brace-expanded pattern. Each item in the row
+ corresponds to a single path-part. For example, the pattern
+ `{a,b/c}/d` would expand to a set of patterns like:
+
+ [ [ a, d ]
+ , [ b, c, d ] ]
+
+ If a portion of the pattern doesn't have any "magic" in it
+ (that is, it's something like `"foo"` rather than `fo*o?`), then it
+ will be left as a string rather than converted to a regular
+ expression.
+
+* `regexp` Created by the `makeRe` method. A single regular expression
+ expressing the entire pattern. This is useful in cases where you wish
+ to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
+* `negate` True if the pattern is negated.
+* `comment` True if the pattern is a comment.
+* `empty` True if the pattern is `""`.
+
+### Methods
+
+* `makeRe` Generate the `regexp` member if necessary, and return it.
+ Will return `false` if the pattern is invalid.
+* `match(fname)` Return true if the filename matches the pattern, or
+ false otherwise.
+* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
+ filename, and match it against a single row in the `regExpSet`. This
+ method is mainly for internal use, but is exposed so that it can be
+ used by a glob-walker that needs to avoid excessive filesystem calls.
+
+All other methods are internal, and will be called as necessary.
+
+### minimatch(path, pattern, options)
+
+Main export. Tests a path against the pattern using the options.
+
+```javascript
+var isJS = minimatch(file, "*.js", { matchBase: true })
+```
+
+### minimatch.filter(pattern, options)
+
+Returns a function that tests its
+supplied argument, suitable for use with `Array.filter`. Example:
+
+```javascript
+var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
+```
+
+### minimatch.match(list, pattern, options)
+
+Match against the list of
+files, in the style of fnmatch or glob. If nothing is matched, and
+options.nonull is set, then return a list containing the pattern itself.
+
+```javascript
+var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
+```
+
+### minimatch.makeRe(pattern, options)
+
+Make a regular expression object from the pattern.
+
+## Options
+
+All options are `false` by default.
+
+### debug
+
+Dump a ton of stuff to stderr.
+
+### nobrace
+
+Do not expand `{a,b}` and `{1..3}` brace sets.
+
+### noglobstar
+
+Disable `**` matching against multiple folder names.
+
+### dot
+
+Allow patterns to match filenames starting with a period, even if
+the pattern does not explicitly have a period in that spot.
+
+Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
+is set.
+
+### noext
+
+Disable "extglob" style patterns like `+(a|b)`.
+
+### nocase
+
+Perform a case-insensitive match.
+
+### nonull
+
+When a match is not found by `minimatch.match`, return a list containing
+the pattern itself if this option is set. When not set, an empty list
+is returned if there are no matches.
+
+### matchBase
+
+If set, then patterns without slashes will be matched
+against the basename of the path if it contains slashes. For example,
+`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
+
+### nocomment
+
+Suppress the behavior of treating `#` at the start of a pattern as a
+comment.
+
+### nonegate
+
+Suppress the behavior of treating a leading `!` character as negation.
+
+### flipNegate
+
+Returns from negate expressions the same as if they were not negated.
+(Ie, true on a hit, false on a miss.)
+
+
+## Comparisons to other fnmatch/glob implementations
+
+While strict compliance with the existing standards is a worthwhile
+goal, some discrepancies exist between minimatch and other
+implementations, and are intentional.
+
+If the pattern starts with a `!` character, then it is negated. Set the
+`nonegate` flag to suppress this behavior, and treat leading `!`
+characters normally. This is perhaps relevant if you wish to start the
+pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
+characters at the start of a pattern will negate the pattern multiple
+times.
+
+If a pattern starts with `#`, then it is treated as a comment, and
+will not match anything. Use `\#` to match a literal `#` at the
+start of a line, or set the `nocomment` flag to suppress this behavior.
+
+The double-star character `**` is supported by default, unless the
+`noglobstar` flag is set. This is supported in the manner of bsdglob
+and bash 4.1, where `**` only has special significance if it is the only
+thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
+`a/**b` will not.
+
+If an escaped pattern has no matches, and the `nonull` flag is set,
+then minimatch.match returns the pattern as-provided, rather than
+interpreting the character escapes. For example,
+`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
+`"*a?"`. This is akin to setting the `nullglob` option in bash, except
+that it does not resolve escaped pattern characters.
+
+If brace expansion is not disabled, then it is performed before any
+other interpretation of the glob pattern. Thus, a pattern like
+`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
+**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
+checked for validity. Since those two are valid, matching proceeds.
diff --git a/node_modules/dir-compare/node_modules/minimatch/minimatch.js b/node_modules/dir-compare/node_modules/minimatch/minimatch.js
new file mode 100644
index 0000000..5b5f8cf
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/minimatch/minimatch.js
@@ -0,0 +1,923 @@
+module.exports = minimatch
+minimatch.Minimatch = Minimatch
+
+var path = { sep: '/' }
+try {
+ path = require('path')
+} catch (er) {}
+
+var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
+var expand = require('brace-expansion')
+
+var plTypes = {
+ '!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
+ '?': { open: '(?:', close: ')?' },
+ '+': { open: '(?:', close: ')+' },
+ '*': { open: '(?:', close: ')*' },
+ '@': { open: '(?:', close: ')' }
+}
+
+// any single thing other than /
+// don't need to escape / when using new RegExp()
+var qmark = '[^/]'
+
+// * => any number of characters
+var star = qmark + '*?'
+
+// ** when dots are allowed. Anything goes, except .. and .
+// not (^ or / followed by one or two dots followed by $ or /),
+// followed by anything, any number of times.
+var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
+
+// not a ^ or / followed by a dot,
+// followed by anything, any number of times.
+var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
+
+// characters that need to be escaped in RegExp.
+var reSpecials = charSet('().*{}+?[]^$\\!')
+
+// "abc" -> { a:true, b:true, c:true }
+function charSet (s) {
+ return s.split('').reduce(function (set, c) {
+ set[c] = true
+ return set
+ }, {})
+}
+
+// normalizes slashes.
+var slashSplit = /\/+/
+
+minimatch.filter = filter
+function filter (pattern, options) {
+ options = options || {}
+ return function (p, i, list) {
+ return minimatch(p, pattern, options)
+ }
+}
+
+function ext (a, b) {
+ a = a || {}
+ b = b || {}
+ var t = {}
+ Object.keys(b).forEach(function (k) {
+ t[k] = b[k]
+ })
+ Object.keys(a).forEach(function (k) {
+ t[k] = a[k]
+ })
+ return t
+}
+
+minimatch.defaults = function (def) {
+ if (!def || !Object.keys(def).length) return minimatch
+
+ var orig = minimatch
+
+ var m = function minimatch (p, pattern, options) {
+ return orig.minimatch(p, pattern, ext(def, options))
+ }
+
+ m.Minimatch = function Minimatch (pattern, options) {
+ return new orig.Minimatch(pattern, ext(def, options))
+ }
+
+ return m
+}
+
+Minimatch.defaults = function (def) {
+ if (!def || !Object.keys(def).length) return Minimatch
+ return minimatch.defaults(def).Minimatch
+}
+
+function minimatch (p, pattern, options) {
+ if (typeof pattern !== 'string') {
+ throw new TypeError('glob pattern string required')
+ }
+
+ if (!options) options = {}
+
+ // shortcut: comments match nothing.
+ if (!options.nocomment && pattern.charAt(0) === '#') {
+ return false
+ }
+
+ // "" only matches ""
+ if (pattern.trim() === '') return p === ''
+
+ return new Minimatch(pattern, options).match(p)
+}
+
+function Minimatch (pattern, options) {
+ if (!(this instanceof Minimatch)) {
+ return new Minimatch(pattern, options)
+ }
+
+ if (typeof pattern !== 'string') {
+ throw new TypeError('glob pattern string required')
+ }
+
+ if (!options) options = {}
+ pattern = pattern.trim()
+
+ // windows support: need to use /, not \
+ if (path.sep !== '/') {
+ pattern = pattern.split(path.sep).join('/')
+ }
+
+ this.options = options
+ this.set = []
+ this.pattern = pattern
+ this.regexp = null
+ this.negate = false
+ this.comment = false
+ this.empty = false
+
+ // make the set of regexps etc.
+ this.make()
+}
+
+Minimatch.prototype.debug = function () {}
+
+Minimatch.prototype.make = make
+function make () {
+ // don't do it more than once.
+ if (this._made) return
+
+ var pattern = this.pattern
+ var options = this.options
+
+ // empty patterns and comments match nothing.
+ if (!options.nocomment && pattern.charAt(0) === '#') {
+ this.comment = true
+ return
+ }
+ if (!pattern) {
+ this.empty = true
+ return
+ }
+
+ // step 1: figure out negation, etc.
+ this.parseNegate()
+
+ // step 2: expand braces
+ var set = this.globSet = this.braceExpand()
+
+ if (options.debug) this.debug = console.error
+
+ this.debug(this.pattern, set)
+
+ // step 3: now we have a set, so turn each one into a series of path-portion
+ // matching patterns.
+ // These will be regexps, except in the case of "**", which is
+ // set to the GLOBSTAR object for globstar behavior,
+ // and will not contain any / characters
+ set = this.globParts = set.map(function (s) {
+ return s.split(slashSplit)
+ })
+
+ this.debug(this.pattern, set)
+
+ // glob --> regexps
+ set = set.map(function (s, si, set) {
+ return s.map(this.parse, this)
+ }, this)
+
+ this.debug(this.pattern, set)
+
+ // filter out everything that didn't compile properly.
+ set = set.filter(function (s) {
+ return s.indexOf(false) === -1
+ })
+
+ this.debug(this.pattern, set)
+
+ this.set = set
+}
+
+Minimatch.prototype.parseNegate = parseNegate
+function parseNegate () {
+ var pattern = this.pattern
+ var negate = false
+ var options = this.options
+ var negateOffset = 0
+
+ if (options.nonegate) return
+
+ for (var i = 0, l = pattern.length
+ ; i < l && pattern.charAt(i) === '!'
+ ; i++) {
+ negate = !negate
+ negateOffset++
+ }
+
+ if (negateOffset) this.pattern = pattern.substr(negateOffset)
+ this.negate = negate
+}
+
+// Brace expansion:
+// a{b,c}d -> abd acd
+// a{b,}c -> abc ac
+// a{0..3}d -> a0d a1d a2d a3d
+// a{b,c{d,e}f}g -> abg acdfg acefg
+// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
+//
+// Invalid sets are not expanded.
+// a{2..}b -> a{2..}b
+// a{b}c -> a{b}c
+minimatch.braceExpand = function (pattern, options) {
+ return braceExpand(pattern, options)
+}
+
+Minimatch.prototype.braceExpand = braceExpand
+
+function braceExpand (pattern, options) {
+ if (!options) {
+ if (this instanceof Minimatch) {
+ options = this.options
+ } else {
+ options = {}
+ }
+ }
+
+ pattern = typeof pattern === 'undefined'
+ ? this.pattern : pattern
+
+ if (typeof pattern === 'undefined') {
+ throw new TypeError('undefined pattern')
+ }
+
+ if (options.nobrace ||
+ !pattern.match(/\{.*\}/)) {
+ // shortcut. no need to expand.
+ return [pattern]
+ }
+
+ return expand(pattern)
+}
+
+// parse a component of the expanded set.
+// At this point, no pattern may contain "/" in it
+// so we're going to return a 2d array, where each entry is the full
+// pattern, split on '/', and then turned into a regular expression.
+// A regexp is made at the end which joins each array with an
+// escaped /, and another full one which joins each regexp with |.
+//
+// Following the lead of Bash 4.1, note that "**" only has special meaning
+// when it is the *only* thing in a path portion. Otherwise, any series
+// of * is equivalent to a single *. Globstar behavior is enabled by
+// default, and can be disabled by setting options.noglobstar.
+Minimatch.prototype.parse = parse
+var SUBPARSE = {}
+function parse (pattern, isSub) {
+ if (pattern.length > 1024 * 64) {
+ throw new TypeError('pattern is too long')
+ }
+
+ var options = this.options
+
+ // shortcuts
+ if (!options.noglobstar && pattern === '**') return GLOBSTAR
+ if (pattern === '') return ''
+
+ var re = ''
+ var hasMagic = !!options.nocase
+ var escaping = false
+ // ? => one single character
+ var patternListStack = []
+ var negativeLists = []
+ var stateChar
+ var inClass = false
+ var reClassStart = -1
+ var classStart = -1
+ // . and .. never match anything that doesn't start with .,
+ // even when options.dot is set.
+ var patternStart = pattern.charAt(0) === '.' ? '' // anything
+ // not (start or / followed by . or .. followed by / or end)
+ : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
+ : '(?!\\.)'
+ var self = this
+
+ function clearStateChar () {
+ if (stateChar) {
+ // we had some state-tracking character
+ // that wasn't consumed by this pass.
+ switch (stateChar) {
+ case '*':
+ re += star
+ hasMagic = true
+ break
+ case '?':
+ re += qmark
+ hasMagic = true
+ break
+ default:
+ re += '\\' + stateChar
+ break
+ }
+ self.debug('clearStateChar %j %j', stateChar, re)
+ stateChar = false
+ }
+ }
+
+ for (var i = 0, len = pattern.length, c
+ ; (i < len) && (c = pattern.charAt(i))
+ ; i++) {
+ this.debug('%s\t%s %s %j', pattern, i, re, c)
+
+ // skip over any that are escaped.
+ if (escaping && reSpecials[c]) {
+ re += '\\' + c
+ escaping = false
+ continue
+ }
+
+ switch (c) {
+ case '/':
+ // completely not allowed, even escaped.
+ // Should already be path-split by now.
+ return false
+
+ case '\\':
+ clearStateChar()
+ escaping = true
+ continue
+
+ // the various stateChar values
+ // for the "extglob" stuff.
+ case '?':
+ case '*':
+ case '+':
+ case '@':
+ case '!':
+ this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
+
+ // all of those are literals inside a class, except that
+ // the glob [!a] means [^a] in regexp
+ if (inClass) {
+ this.debug(' in class')
+ if (c === '!' && i === classStart + 1) c = '^'
+ re += c
+ continue
+ }
+
+ // if we already have a stateChar, then it means
+ // that there was something like ** or +? in there.
+ // Handle the stateChar, then proceed with this one.
+ self.debug('call clearStateChar %j', stateChar)
+ clearStateChar()
+ stateChar = c
+ // if extglob is disabled, then +(asdf|foo) isn't a thing.
+ // just clear the statechar *now*, rather than even diving into
+ // the patternList stuff.
+ if (options.noext) clearStateChar()
+ continue
+
+ case '(':
+ if (inClass) {
+ re += '('
+ continue
+ }
+
+ if (!stateChar) {
+ re += '\\('
+ continue
+ }
+
+ patternListStack.push({
+ type: stateChar,
+ start: i - 1,
+ reStart: re.length,
+ open: plTypes[stateChar].open,
+ close: plTypes[stateChar].close
+ })
+ // negation is (?:(?!js)[^/]*)
+ re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
+ this.debug('plType %j %j', stateChar, re)
+ stateChar = false
+ continue
+
+ case ')':
+ if (inClass || !patternListStack.length) {
+ re += '\\)'
+ continue
+ }
+
+ clearStateChar()
+ hasMagic = true
+ var pl = patternListStack.pop()
+ // negation is (?:(?!js)[^/]*)
+ // The others are (?:<pattern>)<type>
+ re += pl.close
+ if (pl.type === '!') {
+ negativeLists.push(pl)
+ }
+ pl.reEnd = re.length
+ continue
+
+ case '|':
+ if (inClass || !patternListStack.length || escaping) {
+ re += '\\|'
+ escaping = false
+ continue
+ }
+
+ clearStateChar()
+ re += '|'
+ continue
+
+ // these are mostly the same in regexp and glob
+ case '[':
+ // swallow any state-tracking char before the [
+ clearStateChar()
+
+ if (inClass) {
+ re += '\\' + c
+ continue
+ }
+
+ inClass = true
+ classStart = i
+ reClassStart = re.length
+ re += c
+ continue
+
+ case ']':
+ // a right bracket shall lose its special
+ // meaning and represent itself in
+ // a bracket expression if it occurs
+ // first in the list. -- POSIX.2 2.8.3.2
+ if (i === classStart + 1 || !inClass) {
+ re += '\\' + c
+ escaping = false
+ continue
+ }
+
+ // handle the case where we left a class open.
+ // "[z-a]" is valid, equivalent to "\[z-a\]"
+ if (inClass) {
+ // split where the last [ was, make sure we don't have
+ // an invalid re. if so, re-walk the contents of the
+ // would-be class to re-translate any characters that
+ // were passed through as-is
+ // TODO: It would probably be faster to determine this
+ // without a try/catch and a new RegExp, but it's tricky
+ // to do safely. For now, this is safe and works.
+ var cs = pattern.substring(classStart + 1, i)
+ try {
+ RegExp('[' + cs + ']')
+ } catch (er) {
+ // not a valid class!
+ var sp = this.parse(cs, SUBPARSE)
+ re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
+ hasMagic = hasMagic || sp[1]
+ inClass = false
+ continue
+ }
+ }
+
+ // finish up the class.
+ hasMagic = true
+ inClass = false
+ re += c
+ continue
+
+ default:
+ // swallow any state char that wasn't consumed
+ clearStateChar()
+
+ if (escaping) {
+ // no need
+ escaping = false
+ } else if (reSpecials[c]
+ && !(c === '^' && inClass)) {
+ re += '\\'
+ }
+
+ re += c
+
+ } // switch
+ } // for
+
+ // handle the case where we left a class open.
+ // "[abc" is valid, equivalent to "\[abc"
+ if (inClass) {
+ // split where the last [ was, and escape it
+ // this is a huge pita. We now have to re-walk
+ // the contents of the would-be class to re-translate
+ // any characters that were passed through as-is
+ cs = pattern.substr(classStart + 1)
+ sp = this.parse(cs, SUBPARSE)
+ re = re.substr(0, reClassStart) + '\\[' + sp[0]
+ hasMagic = hasMagic || sp[1]
+ }
+
+ // handle the case where we had a +( thing at the *end*
+ // of the pattern.
+ // each pattern list stack adds 3 chars, and we need to go through
+ // and escape any | chars that were passed through as-is for the regexp.
+ // Go through and escape them, taking care not to double-escape any
+ // | chars that were already escaped.
+ for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
+ var tail = re.slice(pl.reStart + pl.open.length)
+ this.debug('setting tail', re, pl)
+ // maybe some even number of \, then maybe 1 \, followed by a |
+ tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {
+ if (!$2) {
+ // the | isn't already escaped, so escape it.
+ $2 = '\\'
+ }
+
+ // need to escape all those slashes *again*, without escaping the
+ // one that we need for escaping the | character. As it works out,
+ // escaping an even number of slashes can be done by simply repeating
+ // it exactly after itself. That's why this trick works.
+ //
+ // I am sorry that you have to see this.
+ return $1 + $1 + $2 + '|'
+ })
+
+ this.debug('tail=%j\n %s', tail, tail, pl, re)
+ var t = pl.type === '*' ? star
+ : pl.type === '?' ? qmark
+ : '\\' + pl.type
+
+ hasMagic = true
+ re = re.slice(0, pl.reStart) + t + '\\(' + tail
+ }
+
+ // handle trailing things that only matter at the very end.
+ clearStateChar()
+ if (escaping) {
+ // trailing \\
+ re += '\\\\'
+ }
+
+ // only need to apply the nodot start if the re starts with
+ // something that could conceivably capture a dot
+ var addPatternStart = false
+ switch (re.charAt(0)) {
+ case '.':
+ case '[':
+ case '(': addPatternStart = true
+ }
+
+ // Hack to work around lack of negative lookbehind in JS
+ // A pattern like: *.!(x).!(y|z) needs to ensure that a name
+ // like 'a.xyz.yz' doesn't match. So, the first negative
+ // lookahead, has to look ALL the way ahead, to the end of
+ // the pattern.
+ for (var n = negativeLists.length - 1; n > -1; n--) {
+ var nl = negativeLists[n]
+
+ var nlBefore = re.slice(0, nl.reStart)
+ var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
+ var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
+ var nlAfter = re.slice(nl.reEnd)
+
+ nlLast += nlAfter
+
+ // Handle nested stuff like *(*.js|!(*.json)), where open parens
+ // mean that we should *not* include the ) in the bit that is considered
+ // "after" the negated section.
+ var openParensBefore = nlBefore.split('(').length - 1
+ var cleanAfter = nlAfter
+ for (i = 0; i < openParensBefore; i++) {
+ cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
+ }
+ nlAfter = cleanAfter
+
+ var dollar = ''
+ if (nlAfter === '' && isSub !== SUBPARSE) {
+ dollar = '$'
+ }
+ var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
+ re = newRe
+ }
+
+ // if the re is not "" at this point, then we need to make sure
+ // it doesn't match against an empty path part.
+ // Otherwise a/* will match a/, which it should not.
+ if (re !== '' && hasMagic) {
+ re = '(?=.)' + re
+ }
+
+ if (addPatternStart) {
+ re = patternStart + re
+ }
+
+ // parsing just a piece of a larger pattern.
+ if (isSub === SUBPARSE) {
+ return [re, hasMagic]
+ }
+
+ // skip the regexp for non-magical patterns
+ // unescape anything in it, though, so that it'll be
+ // an exact match against a file etc.
+ if (!hasMagic) {
+ return globUnescape(pattern)
+ }
+
+ var flags = options.nocase ? 'i' : ''
+ try {
+ var regExp = new RegExp('^' + re + '$', flags)
+ } catch (er) {
+ // If it was an invalid regular expression, then it can't match
+ // anything. This trick looks for a character after the end of
+ // the string, which is of course impossible, except in multi-line
+ // mode, but it's not a /m regex.
+ return new RegExp('$.')
+ }
+
+ regExp._glob = pattern
+ regExp._src = re
+
+ return regExp
+}
+
+minimatch.makeRe = function (pattern, options) {
+ return new Minimatch(pattern, options || {}).makeRe()
+}
+
+Minimatch.prototype.makeRe = makeRe
+function makeRe () {
+ if (this.regexp || this.regexp === false) return this.regexp
+
+ // at this point, this.set is a 2d array of partial
+ // pattern strings, or "**".
+ //
+ // It's better to use .match(). This function shouldn't
+ // be used, really, but it's pretty convenient sometimes,
+ // when you just want to work with a regex.
+ var set = this.set
+
+ if (!set.length) {
+ this.regexp = false
+ return this.regexp
+ }
+ var options = this.options
+
+ var twoStar = options.noglobstar ? star
+ : options.dot ? twoStarDot
+ : twoStarNoDot
+ var flags = options.nocase ? 'i' : ''
+
+ var re = set.map(function (pattern) {
+ return pattern.map(function (p) {
+ return (p === GLOBSTAR) ? twoStar
+ : (typeof p === 'string') ? regExpEscape(p)
+ : p._src
+ }).join('\\\/')
+ }).join('|')
+
+ // must match entire pattern
+ // ending in a * or ** will make it less strict.
+ re = '^(?:' + re + ')$'
+
+ // can match anything, as long as it's not this.
+ if (this.negate) re = '^(?!' + re + ').*$'
+
+ try {
+ this.regexp = new RegExp(re, flags)
+ } catch (ex) {
+ this.regexp = false
+ }
+ return this.regexp
+}
+
+minimatch.match = function (list, pattern, options) {
+ options = options || {}
+ var mm = new Minimatch(pattern, options)
+ list = list.filter(function (f) {
+ return mm.match(f)
+ })
+ if (mm.options.nonull && !list.length) {
+ list.push(pattern)
+ }
+ return list
+}
+
+Minimatch.prototype.match = match
+function match (f, partial) {
+ this.debug('match', f, this.pattern)
+ // short-circuit in the case of busted things.
+ // comments, etc.
+ if (this.comment) return false
+ if (this.empty) return f === ''
+
+ if (f === '/' && partial) return true
+
+ var options = this.options
+
+ // windows: need to use /, not \
+ if (path.sep !== '/') {
+ f = f.split(path.sep).join('/')
+ }
+
+ // treat the test path as a set of pathparts.
+ f = f.split(slashSplit)
+ this.debug(this.pattern, 'split', f)
+
+ // just ONE of the pattern sets in this.set needs to match
+ // in order for it to be valid. If negating, then just one
+ // match means that we have failed.
+ // Either way, return on the first hit.
+
+ var set = this.set
+ this.debug(this.pattern, 'set', set)
+
+ // Find the basename of the path by looking for the last non-empty segment
+ var filename
+ var i
+ for (i = f.length - 1; i >= 0; i--) {
+ filename = f[i]
+ if (filename) break
+ }
+
+ for (i = 0; i < set.length; i++) {
+ var pattern = set[i]
+ var file = f
+ if (options.matchBase && pattern.length === 1) {
+ file = [filename]
+ }
+ var hit = this.matchOne(file, pattern, partial)
+ if (hit) {
+ if (options.flipNegate) return true
+ return !this.negate
+ }
+ }
+
+ // didn't get any hits. this is success if it's a negative
+ // pattern, failure otherwise.
+ if (options.flipNegate) return false
+ return this.negate
+}
+
+// set partial to true to test if, for example,
+// "/a/b" matches the start of "/*/b/*/d"
+// Partial means, if you run out of file before you run
+// out of pattern, then that's fine, as long as all
+// the parts match.
+Minimatch.prototype.matchOne = function (file, pattern, partial) {
+ var options = this.options
+
+ this.debug('matchOne',
+ { 'this': this, file: file, pattern: pattern })
+
+ this.debug('matchOne', file.length, pattern.length)
+
+ for (var fi = 0,
+ pi = 0,
+ fl = file.length,
+ pl = pattern.length
+ ; (fi < fl) && (pi < pl)
+ ; fi++, pi++) {
+ this.debug('matchOne loop')
+ var p = pattern[pi]
+ var f = file[fi]
+
+ this.debug(pattern, p, f)
+
+ // should be impossible.
+ // some invalid regexp stuff in the set.
+ if (p === false) return false
+
+ if (p === GLOBSTAR) {
+ this.debug('GLOBSTAR', [pattern, p, f])
+
+ // "**"
+ // a/**/b/**/c would match the following:
+ // a/b/x/y/z/c
+ // a/x/y/z/b/c
+ // a/b/x/b/x/c
+ // a/b/c
+ // To do this, take the rest of the pattern after
+ // the **, and see if it would match the file remainder.
+ // If so, return success.
+ // If not, the ** "swallows" a segment, and try again.
+ // This is recursively awful.
+ //
+ // a/**/b/**/c matching a/b/x/y/z/c
+ // - a matches a
+ // - doublestar
+ // - matchOne(b/x/y/z/c, b/**/c)
+ // - b matches b
+ // - doublestar
+ // - matchOne(x/y/z/c, c) -> no
+ // - matchOne(y/z/c, c) -> no
+ // - matchOne(z/c, c) -> no
+ // - matchOne(c, c) yes, hit
+ var fr = fi
+ var pr = pi + 1
+ if (pr === pl) {
+ this.debug('** at the end')
+ // a ** at the end will just swallow the rest.
+ // We have found a match.
+ // however, it will not swallow /.x, unless
+ // options.dot is set.
+ // . and .. are *never* matched by **, for explosively
+ // exponential reasons.
+ for (; fi < fl; fi++) {
+ if (file[fi] === '.' || file[fi] === '..' ||
+ (!options.dot && file[fi].charAt(0) === '.')) return false
+ }
+ return true
+ }
+
+ // ok, let's see if we can swallow whatever we can.
+ while (fr < fl) {
+ var swallowee = file[fr]
+
+ this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
+
+ // XXX remove this slice. Just pass the start index.
+ if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
+ this.debug('globstar found match!', fr, fl, swallowee)
+ // found a match.
+ return true
+ } else {
+ // can't swallow "." or ".." ever.
+ // can only swallow ".foo" when explicitly asked.
+ if (swallowee === '.' || swallowee === '..' ||
+ (!options.dot && swallowee.charAt(0) === '.')) {
+ this.debug('dot detected!', file, fr, pattern, pr)
+ break
+ }
+
+ // ** swallows a segment, and continue.
+ this.debug('globstar swallow a segment, and continue')
+ fr++
+ }
+ }
+
+ // no match was found.
+ // However, in partial mode, we can't say this is necessarily over.
+ // If there's more *pattern* left, then
+ if (partial) {
+ // ran out of file
+ this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
+ if (fr === fl) return true
+ }
+ return false
+ }
+
+ // something other than **
+ // non-magic patterns just have to match exactly
+ // patterns with magic have been turned into regexps.
+ var hit
+ if (typeof p === 'string') {
+ if (options.nocase) {
+ hit = f.toLowerCase() === p.toLowerCase()
+ } else {
+ hit = f === p
+ }
+ this.debug('string match', p, f, hit)
+ } else {
+ hit = f.match(p)
+ this.debug('pattern match', p, f, hit)
+ }
+
+ if (!hit) return false
+ }
+
+ // Note: ending in / means that we'll get a final ""
+ // at the end of the pattern. This can only match a
+ // corresponding "" at the end of the file.
+ // If the file ends in /, then it can only match a
+ // a pattern that ends in /, unless the pattern just
+ // doesn't have any more for it. But, a/b/ should *not*
+ // match "a/b/*", even though "" matches against the
+ // [^/]*? pattern, except in partial mode, where it might
+ // simply not be reached yet.
+ // However, a/b/ should still satisfy a/*
+
+ // now either we fell off the end of the pattern, or we're done.
+ if (fi === fl && pi === pl) {
+ // ran out of pattern and filename at the same time.
+ // an exact hit!
+ return true
+ } else if (fi === fl) {
+ // ran out of file, but still had pattern left.
+ // this is ok if we're doing the match as part of
+ // a glob fs traversal.
+ return partial
+ } else if (pi === pl) {
+ // ran out of pattern, still have file left.
+ // this is only acceptable if we're on the very last
+ // empty segment of a file with a trailing slash.
+ // a/* should match a/b/
+ var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
+ return emptyFileEnd
+ }
+
+ // should be unreachable.
+ throw new Error('wtf?')
+}
+
+// replace stuff like \* with *
+function globUnescape (s) {
+ return s.replace(/\\(.)/g, '$1')
+}
+
+function regExpEscape (s) {
+ return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
+}
diff --git a/node_modules/dir-compare/node_modules/minimatch/package.json b/node_modules/dir-compare/node_modules/minimatch/package.json
new file mode 100644
index 0000000..c4514c8
--- /dev/null
+++ b/node_modules/dir-compare/node_modules/minimatch/package.json
@@ -0,0 +1,30 @@
+{
+ "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
+ "name": "minimatch",
+ "description": "a glob matcher in javascript",
+ "version": "3.0.4",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/minimatch.git"
+ },
+ "main": "minimatch.js",
+ "scripts": {
+ "test": "tap test/*.js --cov",
+ "preversion": "npm test",
+ "postversion": "npm publish",
+ "postpublish": "git push origin --all; git push origin --tags"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "devDependencies": {
+ "tap": "^10.3.2"
+ },
+ "license": "ISC",
+ "files": [
+ "minimatch.js"
+ ]
+}
diff --git a/node_modules/dir-compare/package.json b/node_modules/dir-compare/package.json
new file mode 100644
index 0000000..d5ccb56
--- /dev/null
+++ b/node_modules/dir-compare/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "dir-compare",
+ "version": "2.4.0",
+ "description": "Node JS directory compare",
+ "main": "src/index.js",
+ "types": "src/index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/gliviu/dir-compare"
+ },
+ "keywords": [
+ "compare",
+ "directory",
+ "folder"
+ ],
+ "files": [
+ "src"
+ ],
+ "scripts": {
+ "clean": "rm -rf build && rm -rf .nyc_output && rm -rf coverage",
+ "copydeps": "copyfiles \"test/expected/**\" test/testdir.tar \"test/extended/res/**\" package.json build",
+ "build": "tsc && npm run copydeps",
+ "lint": "tslint -p tsconfig.json",
+ "pretest": "npm install && npm run build",
+ "test": "node build/test/runTests.js",
+ "extest": "npm run pretest && ./test/extended/init.sh && test/extended/runall.sh",
+ "coverage": "npx nyc --exclude \"build/test/**\" --reporter=lcov npm test && npx nyc report",
+ "toc": "npx markdown-toc README.md; echo \n",
+ "docs": "typedoc --includeVersion --includeDeclarations --excludeExternals --theme minimal --mode file --readme none --gitRevision master --toc compare,compareSync,fileCompareHandlers,Options,Result --out docs ./src/index.d.ts"
+ },
+ "dependencies": {
+ "buffer-equal": "1.0.0",
+ "colors": "1.0.3",
+ "commander": "2.9.0",
+ "minimatch": "3.0.4"
+ },
+ "devDependencies": {
+ "@types/node": "^12.11.7",
+ "copyfiles": "^1.2.0",
+ "memory-streams": "0.1.0",
+ "semver": "5.6.0",
+ "shelljs": "0.3.0",
+ "tar-fs": "2.1.1",
+ "temp": "0.9.0",
+ "ts-node": "^8.5.4",
+ "tslint": "^5.20.0",
+ "tslint-config-prettier": "^1.13.0",
+ "typedoc": "0.19.2",
+ "typescript": "^3.7.4"
+ },
+ "bin": {
+ "dircompare": "src/cli/dircompare.js"
+ },
+ "author": "Liviu Grigorescu",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/gliviu/dir-compare/issues"
+ }
+}
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
+}
+