Update gitignore (sorry)
Some checks failed
Android Build / publish (push) Successful in 33s
Linux Build / publish (push) Failing after 25s

This commit is contained in:
olcxja 2026-05-10 14:02:17 +02:00
commit cca8b02fea
6604 changed files with 1219661 additions and 4 deletions

22
electron/node_modules/dir-compare/LICENSE generated vendored Normal file
View file

@ -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.

374
electron/node_modules/dir-compare/README.md generated vendored Normal file
View file

@ -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

View file

@ -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 doesnt 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

View file

@ -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.

View file

@ -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 dont 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

File diff suppressed because it is too large Load diff

View file

@ -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"
}
}

View file

@ -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.

View file

@ -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.

View file

@ -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, '\\$&')
}

View file

@ -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"
]
}

59
electron/node_modules/dir-compare/package.json generated vendored Normal file
View file

@ -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"
}
}

147
electron/node_modules/dir-compare/src/cli/dircompare.js generated vendored Executable file
View file

@ -0,0 +1,147 @@
#!/usr/bin/env node
var program = require('commander')
var dircompare = require('../index')
var fs = require('fs')
var util = require('util')
var print = require('./print')
var pjson = require('../../package.json')
program
.version(pjson.version)
.usage('[options] leftdir rightdir')
.option('-c, --compare-content', 'compare files by content')
.option('-D, --compare-date', 'compare files by date')
.option('--date-tolerance [type]', 'tolerance to be used in date comparison (milliseconds)')
.option('--compare-symlink', 'compare files and directories by symlink')
.option('-f, --filter [type]', 'file name filter', undefined)
.option('-x, --exclude [type]', 'file/directory name exclude filter', undefined)
.option('-S, --skip-subdirs', 'do not recurse into subdirectories')
.option('-L, --skip-symlinks', 'ignore symlinks')
.option('-i, --ignore-case', 'ignores case when comparing file names')
.option('-l, --show-left', 'report - show entries occurring in left dir')
.option('-r, --show-right', 'report - show entries occurring in right dir')
.option('-e, --show-equal', 'report - show identic entries occurring in both dirs')
.option('-d, --show-distinct', 'report - show distinct entries occurring in both dirs')
.option('-a, --show-all', 'report - show all entries')
.option('-w, --whole-report', 'report - include directories in detailed report')
.option('--reason', 'report - show reason when entries are distinct')
.option('--csv', 'report - print details as csv')
.option('--nocolors', 'don\'t use console colors')
.option('--async', 'Make use of multiple cores')
program.on('--help', function () {
console.log(' By default files are compared by size.')
console.log(' --date-tolerance defaults to 1000 ms. Two files are considered to have')
console.log(' the same date if the difference between their modification dates fits')
console.log(' within date tolerance.')
console.log()
console.log(' Exit codes:')
console.log(' 0 - entries are identical')
console.log(' 1 - entries are different')
console.log(' 2 - error occurred')
console.log()
console.log(' Examples:')
console.log(' compare by content dircompare -c dir1 dir2')
console.log(' show only different files dircompare -d dir1 dir2')
console.log()
console.log(' exclude filter dircompare -x ".git,node_modules" dir1 dir2')
console.log(' dircompare -x "/tests/expected" dir1 dir2')
console.log(' dircompare -x "**/expected" dir1 dir2')
console.log(' dircompare -x "**/tests/**/*.ts" dir1 dir2')
console.log()
console.log(' include filter dircompare -f "*.js,*.yml" dir1 dir2')
console.log(' dircompare -f "/tests/**/*.js" dir1 dir2')
console.log(' dircompare -f "**/tests/**/*.ts" dir1 dir2')
})
// Fix for https://github.com/tj/commander.js/issues/125
program.allowUnknownOption()
program.parse(process.argv)
var parsed = program.parseOptions(program.normalize(process.argv.slice(2)))
if (parsed.unknown.length > 0) {
console.error('Unknown options: ' + parsed.unknown)
process.exit(2)
}
var run = function () {
try {
if (program.args.length !== 2) {
program.outputHelp()
process.exit(2)
} else {
var options = {}
options.compareContent = program.compareContent
options.compareDate = program.compareDate
options.compareSymlink = program.compareSymlink
options.compareSize = true
options.skipSubdirs = program.skipSubdirs
options.skipSymlinks = program.skipSymlinks
options.ignoreCase = program.ignoreCase
options.includeFilter = program.filter
options.excludeFilter = program.exclude
options.noDiffSet = !(program.showAll || program.showEqual || program.showLeft || program.showRight || program.showDistinct)
options.dateTolerance = program.dateTolerance || 1000
var async = program.async
var path1 = program.args[0]
var path2 = program.args[1]
var abort = false
if (!isNumeric(options.dateTolerance)) {
console.error("Numeric value expected for --date-tolerance")
abort = true
}
if (!fs.existsSync(path1)) {
console.error(util.format("Path '%s' missing"), path1)
abort = true
}
if (!fs.existsSync(path2)) {
console.error(util.format("Path '%s' missing"), path2)
abort = true
}
if (!abort) {
// compare
var comparePromise
if (async) {
comparePromise = dircompare.compare(path1, path2, options)
} else {
comparePromise = new Promise(function (resolve, reject) {
resolve(dircompare.compareSync(path1, path2, options))
})
}
comparePromise.then(
function (res) {
// PRINT DETAILS
print(res, process.stdout, program)
if (res.same) {
process.exit(0)
} else {
process.exit(1)
}
},
function (error) {
console.error('Error occurred: ' + (error instanceof Error ? error.stack : error))
process.exit(2)
})
} else {
process.exit(2)
}
}
} catch (e) {
console.error(e.stack)
process.exit(2)
}
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
}
run()

192
electron/node_modules/dir-compare/src/cli/print.js generated vendored Normal file
View file

@ -0,0 +1,192 @@
var colors = require('colors')
var util = require('util')
var pathUtils = require('path')
var PATH_SEP = pathUtils.sep
// Prints dir compare results.
// 'program' represents display options and correspond to dircompare command line parameters.
// Example: 'dircompare --show-all --exclude *.js dir1 dir2' translates into
// program: {showAll: true, exclude: '*.js'}
//
var print = function (res, writer, program) {
var noColor = function (str) { return str }
var colorEqual = program.nocolors ? noColor : colors.green
var colorDistinct = program.nocolors ? noColor : colors.red
var colorLeft = noColor
var colorRight = noColor
var colorDir = noColor
var colorBrokenLinks = noColor
var colorMissing = program.nocolors ? noColor : colors.yellow
// calculate relative path length for pretty print
var relativePathMaxLength = 0, fileNameMaxLength = 0
if (!program.csv && res.diffSet) {
res.diffSet.forEach(function (diff) {
if (diff.relativePath.length > relativePathMaxLength) {
relativePathMaxLength = diff.relativePath.length
}
var len = getCompareFile(diff, '??', colorMissing).length
if (len > fileNameMaxLength) {
fileNameMaxLength = len
}
})
}
// csv header
if (program.csv) {
writer.write('path,name,state,type,size1,size2,date1,date2,reason\n')
}
if (res.diffSet) {
for (var i = 0; i < res.diffSet.length; i++) {
var detail = res.diffSet[i]
var color, show = true
if (!program.wholeReport) {
// show only files or broken links
var type = detail.type1 !== 'missing' ? detail.type1 : detail.type2
if (type !== 'file' && type !== 'broken-link') {
show = false
}
}
if (show) {
switch (detail.state) {
case 'equal':
color = colorEqual
show = program.showAll || program.showEqual ? true : false
break
case 'left':
color = colorLeft
show = program.showAll || program.showLeft ? true : false
break
case 'right':
color = colorRight
show = program.showAll || program.showRight ? true : false
break
case 'distinct':
color = colorDistinct
show = program.showAll || program.showDistinct ? true : false
break
default:
show = true
color = colors.gray
}
if (show) {
if (program.csv) {
printCsv(writer, detail, color)
} else {
printPretty(writer, program, detail, color, colorDir, colorMissing, relativePathMaxLength, fileNameMaxLength)
}
}
}
}
}
// PRINT STATISTICS
var statTotal, statEqual, statLeft, statRight, statDistinct
if (program.wholeReport) {
statTotal = res.total
statEqual = res.equal
statLeft = res.left
statRight = res.right
statDistinct = res.distinct
} else {
var brokenLInksStats = res.brokenLinks
statTotal = res.totalFiles + brokenLInksStats.totalBrokenLinks
statEqual = res.equalFiles
statLeft = res.leftFiles + brokenLInksStats.leftBrokenLinks
statRight = res.rightFiles + brokenLInksStats.rightBrokenLinks
statDistinct = res.distinctFiles + brokenLInksStats.distinctBrokenLinks
}
if (!program.noDiffIndicator) {
writer.write(res.same ? colorEqual('Entries are identical\n') : colorDistinct('Entries are different\n'))
}
var stats = util.format('total: %s, equal: %s, distinct: %s, only left: %s, only right: %s',
statTotal,
colorEqual(statEqual),
colorDistinct(statDistinct),
colorLeft(statLeft),
colorRight(statRight)
)
if (res.brokenLinks.totalBrokenLinks > 0) {
stats += util.format(', broken links: %s', colorBrokenLinks(res.brokenLinks.totalBrokenLinks))
}
stats += '\n'
writer.write(stats)
}
/**
* Print details for default view mode
*/
var printPretty = function (writer, program, detail, color, dirColor, missingColor, relativePathMaxLength, fileNameMaxLength) {
var path = detail.relativePath === '' ? PATH_SEP : detail.relativePath
var state
switch (detail.state) {
case 'equal':
state = '=='
break
case 'left':
state = '->'
break
case 'right':
state = '<-'
break
case 'distinct':
state = '<>'
break
default:
state = '?'
}
var type = ''
type = detail.type1 !== 'missing' ? detail.type1 : detail.type2
if (type === 'directory') {
type = dirColor(type)
}
var cmpEntry = getCompareFile(detail, color(state), missingColor)
var reason = ''
if (program.reason && detail.reason) {
reason = util.format(' <%s>', detail.reason)
}
if (program.wholeReport || type === 'broken-link') {
writer.write(util.format('[%s] %s (%s)%s\n', path, cmpEntry, type, reason))
} else {
writer.write(util.format('[%s] %s%s\n', path, cmpEntry, reason))
}
}
var getCompareFile = function (detail, state, missingcolor) {
p1 = detail.name1 ? detail.name1 : ''
p2 = detail.name2 ? detail.name2 : ''
var missing1 = detail.type1 === 'missing' ? missingcolor('missing') : ''
var missing2 = detail.type2 === 'missing' ? missingcolor('missing') : ''
return util.format('%s%s %s %s%s', missing1, p1, state, missing2, p2)
}
/**
* Print csv details.
*/
var printCsv = function (writer, detail, color) {
var size1 = '', size2 = ''
if (detail.type1 === 'file') {
size1 = detail.size1 !== undefined ? detail.size1 : ''
}
if (detail.type2 === 'file') {
size2 = detail.size2 !== undefined ? detail.size2 : ''
}
var date1 = '', date2 = ''
date1 = detail.date1 !== undefined ? detail.date1.toISOString() : ''
date2 = detail.date2 !== undefined ? detail.date2.toISOString() : ''
var type = ''
type = detail.type1 !== 'missing' ? detail.type1 : detail.type2
var path = detail.relativePath ? detail.relativePath : PATH_SEP
var name = (detail.name1 ? detail.name1 : detail.name2)
var reason = detail.reason || ''
writer.write(util.format('%s,%s,%s,%s,%s,%s,%s,%s,%s\n', path, name, color(detail.state), type, size1, size2, date1, date2, reason))
}
module.exports = print

141
electron/node_modules/dir-compare/src/compareAsync.js generated vendored Normal file
View file

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

90
electron/node_modules/dir-compare/src/compareSync.js generated vendored Normal file
View file

@ -0,0 +1,90 @@
var fs = require('fs')
var pathUtils = require('path')
var entryBuilder = require('./entry/entryBuilder')
var entryEquality = require('./entry/entryEquality')
var stats = require('./statistics/statisticsUpdate')
var loopDetector = require('./symlink/loopDetector')
var entryComparator = require('./entry/entryComparator')
var entryType = require('./entry/entryType')
/**
* Returns the sorted list of entries in a directory.
*/
var getEntries = function (rootEntry, relativePath, loopDetected, options) {
if (!rootEntry || loopDetected) {
return []
}
if (rootEntry.isDirectory) {
var entries = fs.readdirSync(rootEntry.absolutePath)
return entryBuilder.buildDirEntries(rootEntry, entries, relativePath, options)
}
return [rootEntry]
}
/**
* Compares two directories synchronously.
*/
var compare = function (rootEntry1, rootEntry2, level, relativePath, options, statistics, diffSet, symlinkCache) {
var loopDetected1 = loopDetector.detectLoop(rootEntry1, symlinkCache.dir1)
var loopDetected2 = loopDetector.detectLoop(rootEntry2, symlinkCache.dir2)
loopDetector.updateSymlinkCache(symlinkCache, rootEntry1, rootEntry2, loopDetected1, loopDetected2)
var entries1 = getEntries(rootEntry1, relativePath, loopDetected1, options)
var entries2 = getEntries(rootEntry2, relativePath, loopDetected2, options)
var i1 = 0, i2 = 0
while (i1 < entries1.length || i2 < entries2.length) {
var entry1 = entries1[i1]
var entry2 = entries2[i2]
var type1, type2
// compare entry name (-1, 0, 1)
var cmp
if (i1 < entries1.length && i2 < entries2.length) {
cmp = entryComparator.compareEntry(entry1, entry2, options)
type1 = entryType.getType(entry1)
type2 = entryType.getType(entry2)
} else if (i1 < entries1.length) {
type1 = entryType.getType(entry1)
type2 = entryType.getType(undefined)
cmp = -1
} else {
type1 = entryType.getType(undefined)
type2 = entryType.getType(entry2)
cmp = 1
}
// process entry
if (cmp === 0) {
// Both left/right exist and have the same name and type
var compareEntryRes = entryEquality.isEntryEqualSync(entry1, entry2, type1, options)
options.resultBuilder(entry1, entry2,
compareEntryRes.same ? 'equal' : 'distinct',
level, relativePath, options, statistics, diffSet,
compareEntryRes.reason)
stats.updateStatisticsBoth(entry1, entry2, compareEntryRes.same, compareEntryRes.reason, type1, statistics, options)
i1++
i2++
if (!options.skipSubdirs && type1 === 'directory') {
compare(entry1, entry2, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, diffSet, loopDetector.cloneSymlinkCache(symlinkCache))
}
} else if (cmp < 0) {
// Right missing
options.resultBuilder(entry1, undefined, 'left', level, relativePath, options, statistics, diffSet)
stats.updateStatisticsLeft(entry1, type1, statistics, options)
i1++
if (type1 === 'directory' && !options.skipSubdirs) {
compare(entry1, undefined, level + 1, pathUtils.join(relativePath, entry1.name), options, statistics, diffSet, loopDetector.cloneSymlinkCache(symlinkCache))
}
} else {
// Left missing
options.resultBuilder(undefined, entry2, 'right', level, relativePath, options, statistics, diffSet)
stats.updateStatisticsRight(entry2, type2, statistics, options)
i2++
if (type2 === 'directory' && !options.skipSubdirs) {
compare(undefined, entry2, level + 1, pathUtils.join(relativePath, entry2.name), options, statistics, diffSet, loopDetector.cloneSymlinkCache(symlinkCache))
}
}
}
}
module.exports = compare

View file

@ -0,0 +1,102 @@
var fs = require('fs')
var minimatch = require('minimatch')
var pathUtils = require('path')
var entryComparator = require('./entryComparator')
var PATH_SEP = pathUtils.sep
module.exports = {
/**
* Returns the sorted list of entries in a directory.
*/
buildDirEntries: function (rootEntry, dirEntries, relativePath, options) {
var res = []
for (var i = 0; i < dirEntries.length; i++) {
var entryName = dirEntries[i]
var entryAbsolutePath = rootEntry.absolutePath + PATH_SEP + entryName
var entryPath = rootEntry.path + PATH_SEP + entryName
var entry = this.buildEntry(entryAbsolutePath, entryPath, entryName)
if (options.skipSymlinks && entry.isSymlink) {
entry.stat = undefined
}
if (filterEntry(entry, relativePath, options)) {
res.push(entry)
}
}
return res.sort((a, b) => entryComparator.compareEntry(a, b, options))
},
buildEntry: function (absolutePath, path, name) {
var stats = getStatIgnoreBrokenLink(absolutePath)
return {
name: name,
absolutePath: absolutePath,
path: path,
stat: stats.stat,
lstat: stats.lstat,
isSymlink: stats.lstat.isSymbolicLink(),
isBrokenLink: stats.isBrokenLink,
isDirectory: stats.stat.isDirectory()
}
},
}
function getStatIgnoreBrokenLink(absolutePath) {
var lstat = fs.lstatSync(absolutePath)
try {
return {
stat: fs.statSync(absolutePath),
lstat: lstat,
isBrokenLink: false
}
} catch (error) {
if (error.code === 'ENOENT') {
return {
stat: lstat,
lstat: lstat,
isBrokenLink: true
}
}
throw error
}
}
/**
* Filter entries by file name. Returns true if the file is to be processed.
*/
function filterEntry(entry, relativePath, options) {
if (entry.isSymlink && options.skipSymlinks) {
return false
}
var path = pathUtils.join(relativePath, entry.name)
if ((entry.stat.isFile() && options.includeFilter) && (!match(path, options.includeFilter))) {
return false
}
if ((options.excludeFilter) && (match(path, options.excludeFilter))) {
return false
}
return true
}
/**
* Matches path by pattern.
*/
function match(path, pattern) {
var patternArray = pattern.split(',')
for (var i = 0; i < patternArray.length; i++) {
var pat = patternArray[i]
if (minimatch(path, pat, { dot: true, matchBase: true })) { //nocase
return true
}
}
return false
}

View file

@ -0,0 +1,20 @@
/**
* Determines order criteria for sorting entries in a directory.
*/
module.exports = {
compareEntry: function (a, b, options) {
if (a.isBrokenLink && b.isBrokenLink) {
return options.compareNameHandler(a.name, b.name, options)
} else if (a.isBrokenLink) {
return -1
} else if (b.isBrokenLink) {
return 1
} else if (a.stat.isDirectory() && b.stat.isFile()) {
return -1
} else if (a.stat.isFile() && b.stat.isDirectory()) {
return 1
} else {
return options.compareNameHandler(a.name, b.name, options)
}
}
}

View file

@ -0,0 +1,135 @@
var fs = require('fs')
/**
* Compares two entries with identical name and type.
*/
module.exports = {
isEntryEqualSync: function (entry1, entry2, type, options) {
if (type === 'file') {
return isFileEqualSync(entry1, entry2, options)
}
if (type === 'directory') {
return isDirectoryEqual(entry1, entry2, options)
}
if (type === 'broken-link') {
return isBrokenLinkEqual()
}
throw new Error('Unexpected type ' + type)
},
isEntryEqualAsync: function (entry1, entry2, type, diffSet, options) {
if (type === 'file') {
return isFileEqualAsync(entry1, entry2, type, diffSet, options)
}
if (type === 'directory') {
return isDirectoryEqual(entry1, entry2, options)
}
if (type === 'broken-link') {
return isBrokenLinkEqual()
}
throw new Error('Unexpected type ' + type)
}
}
function isFileEqualSync(entry1, entry2, options) {
var p1 = entry1 ? entry1.absolutePath : undefined
var p2 = entry2 ? entry2.absolutePath : undefined
if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) {
return { same: false, reason: 'different-symlink' }
}
if (options.compareSize && entry1.stat.size !== entry2.stat.size) {
return { same: false, reason: 'different-size' }
}
if (options.compareDate && !isDateEqual(entry1.stat.mtime, entry2.stat.mtime, options.dateTolerance)) {
return { same: false, reason: 'different-date' }
}
if (options.compareContent && !options.compareFileSync(p1, entry1.stat, p2, entry2.stat, options)) {
return { same: false, reason: 'different-content' }
}
return { same: true }
}
function isFileEqualAsync(entry1, entry2, type, diffSet, options) {
var p1 = entry1 ? entry1.absolutePath : undefined
var p2 = entry2 ? entry2.absolutePath : undefined
if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) {
return { same: false, reason: 'different-symlink' }
}
if (options.compareSize && entry1.stat.size !== entry2.stat.size) {
return { same: false, samePromise: undefined, reason: 'different-size' }
}
if (options.compareDate && !isDateEqual(entry1.stat.mtime, entry2.stat.mtime, options.dateTolerance)) {
return { same: false, samePromise: undefined, reason: 'different-date' }
}
if (options.compareContent) {
var samePromise = undefined
var subDiffSet
if (!options.noDiffSet) {
subDiffSet = []
diffSet.push(subDiffSet)
}
samePromise = options.compareFileAsync(p1, entry1.stat, p2, entry2.stat, options)
.then(function (comparisonResult) {
var same, error
if (typeof (comparisonResult) === "boolean") {
same = comparisonResult
} else {
error = comparisonResult
}
return {
entry1: entry1, entry2: entry2, same: same,
error: error, type1: type, type2: type,
diffSet: subDiffSet,
reason: same ? undefined : 'different-content'
}
})
.catch(function (error) {
return {
error: error
}
})
return { same: undefined, samePromise: samePromise }
}
return { same: true, samePromise: undefined }
}
function isDirectoryEqual(entry1, entry2, options) {
if (options.compareSymlink && !isSymlinkEqual(entry1, entry2)) {
return { same: false, reason: 'different-symlink' }
}
return { same: true }
}
function isBrokenLinkEqual() {
return { same: false, reason: 'broken-link' } // broken links are never considered equal
}
/**
* Compares two dates and returns true/false depending on tolerance (milliseconds).
* Two dates are considered equal if the difference in milliseconds between them is less or equal than tolerance.
*/
function isDateEqual(date1, date2, tolerance) {
return Math.abs(date1.getTime() - date2.getTime()) <= tolerance ? true : false
}
/**
* Compares two entries for symlink equality.
*/
function isSymlinkEqual(entry1, entry2) {
if (!entry1.isSymlink && !entry2.isSymlink) {
return true
}
if (entry1.isSymlink && entry2.isSymlink && hasIdenticalLink(entry1.absolutePath, entry2.absolutePath)) {
return true
}
return false
}
function hasIdenticalLink(path1, path2) {
return fs.readlinkSync(path1) === fs.readlinkSync(path2)
}

View file

@ -0,0 +1,18 @@
module.exports = {
/**
* One of 'missing','file','directory','broken-link'
*/
getType: function (entry) {
if (!entry) {
return 'missing'
}
if (entry.isBrokenLink) {
return 'broken-link'
}
if (entry.isDirectory) {
return 'directory'
}
return 'file'
}
}

View file

@ -0,0 +1,28 @@
var fs = require('fs')
var closeFilesSync = function (fd1, fd2) {
if (fd1) {
fs.closeSync(fd1)
}
if (fd2) {
fs.closeSync(fd2)
}
}
var closeFilesAsync = function (fd1, fd2, fdQueue) {
if (fd1 && fd2) {
return fdQueue.promises.close(fd1).then(() => fdQueue.promises.close(fd2))
}
if (fd1) {
return fdQueue.promises.close(fd1)
}
if (fd2) {
return fdQueue.promises.close(fd2)
}
}
module.exports = {
closeFilesSync: closeFilesSync,
closeFilesAsync: closeFilesAsync
}

View file

@ -0,0 +1,113 @@
var fs = require('fs')
var bufferEqual = require('buffer-equal')
var FileDescriptorQueue = require('../fs/FileDescriptorQueue')
var closeFilesSync = require('./closeFile').closeFilesSync
var closeFilesAsync = require('./closeFile').closeFilesAsync
var fsPromise = require('../fs/fsPromise')
var BufferPool = require('../fs/BufferPool')
var MAX_CONCURRENT_FILE_COMPARE = 8
var BUF_SIZE = 100000
var fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2)
var bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE); // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently
/**
* Compares two partial buffers.
*/
var compareBuffers = function (buf1, buf2, contentSize) {
return bufferEqual(buf1.slice(0, contentSize), buf2.slice(0, contentSize))
}
/**
* Compares two files by content.
*/
var compareSync = function (path1, stat1, path2, stat2, options) {
var fd1, fd2
if (stat1.size !== stat2.size) {
return false
}
var bufferPair = bufferPool.allocateBuffers()
try {
fd1 = fs.openSync(path1, 'r')
fd2 = fs.openSync(path2, 'r')
var buf1 = bufferPair.buf1
var buf2 = bufferPair.buf2
var progress = 0
while (true) {
var size1 = fs.readSync(fd1, buf1, 0, BUF_SIZE, null)
var size2 = fs.readSync(fd2, buf2, 0, BUF_SIZE, null)
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
}
}
} finally {
closeFilesSync(fd1, fd2)
bufferPool.freeBuffers(bufferPair)
}
}
/**
* Compares two files by content
*/
var compareAsync = function (path1, stat1, path2, stat2, options) {
var fd1, fd2
var bufferPair
if (stat1.size !== stat2.size) {
return Promise.resolve(false)
}
return Promise.all([fdQueue.promises.open(path1, 'r'), fdQueue.promises.open(path2, 'r')])
.then(function (fds) {
bufferPair = bufferPool.allocateBuffers()
fd1 = fds[0]
fd2 = fds[1]
var buf1 = bufferPair.buf1
var buf2 = bufferPair.buf2
var progress = 0
var compareAsyncInternal = function () {
return Promise.all([
fsPromise.read(fd1, buf1, 0, BUF_SIZE, null),
fsPromise.read(fd2, buf2, 0, BUF_SIZE, null)
]).then(function (bufferSizes) {
var size1 = bufferSizes[0]
var size2 = bufferSizes[1]
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
} else {
return compareAsyncInternal()
}
})
}
return compareAsyncInternal()
})
.then(
// 'finally' polyfill for node 8 and below
function (res) {
return finalizeAsync(fd1, fd2, bufferPair).then(() => res)
},
function (err) {
return finalizeAsync(fd1, fd2, bufferPair).then(() => { throw err; })
}
)
}
function finalizeAsync(fd1, fd2, bufferPair) {
bufferPool.freeBuffers(bufferPair)
return closeFilesAsync(fd1, fd2, fdQueue)
}
module.exports = {
compareSync: compareSync,
compareAsync: compareAsync
}

View file

@ -0,0 +1,196 @@
/**
* Compare files line by line with options to ignore
* line endings and white space differencies.
*/
var fs = require('fs')
var FileDescriptorQueue = require('../fs/FileDescriptorQueue')
var closeFilesSync = require('./closeFile').closeFilesSync
var closeFilesAsync = require('./closeFile').closeFilesAsync
var fsPromise = require('../fs/fsPromise')
var BufferPool = require('../fs/BufferPool')
const LINE_TOKENIZER_REGEXP = /[^\n]+\n?|\n/g
const TRIM_LINE_ENDING_REGEXP = /\r\n$/g
const SPLIT_CONTENT_AND_LINE_ENDING_REGEXP = /([^\r\n]*)([\r\n]*)/
const TRIM_WHITE_SPACES_REGEXP = /^[ \f\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+|[ \f\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+$/g
var MAX_CONCURRENT_FILE_COMPARE = 8
var BUF_SIZE = 100000
var fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2)
var bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE); // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently
function compareSync(path1, stat1, path2, stat2, options) {
var fd1, fd2
var bufferPair = bufferPool.allocateBuffers()
var bufferSize = options.lineBasedHandlerBufferSize || BUF_SIZE
try {
fd1 = fs.openSync(path1, 'r')
fd2 = fs.openSync(path2, 'r')
var buf1 = bufferPair.buf1
var buf2 = bufferPair.buf2
var nextPosition1 = 0, nextPosition2 = 0
while (true) {
var lines1 = readLinesSync(fd1, buf1, bufferSize, nextPosition1)
var lines2 = readLinesSync(fd2, buf2, bufferSize, nextPosition2)
if (lines1.length === 0 && lines2.length === 0) {
// End of file reached
return true
}
var equalLines = compareLines(lines1, lines2, options)
if (equalLines === 0) {
return false
}
nextPosition1 += calculateSize(lines1, equalLines)
nextPosition2 += calculateSize(lines2, equalLines)
}
} finally {
closeFilesSync(fd1, fd2)
bufferPool.freeBuffers(bufferPair)
}
}
async function compareAsync(path1, stat1, path2, stat2, options) {
var fd1, fd2
var bufferSize = options.lineBasedHandlerBufferSize || BUF_SIZE
var bufferPair
try {
var fds = await Promise.all([fdQueue.promises.open(path1, 'r'), fdQueue.promises.open(path2, 'r')])
bufferPair = bufferPool.allocateBuffers()
fd1 = fds[0]
fd2 = fds[1]
var buf1 = bufferPair.buf1
var buf2 = bufferPair.buf2
var nextPosition1 = 0, nextPosition2 = 0
while (true) {
var lines1 = await readLinesAsync(fd1, buf1, bufferSize, nextPosition1)
var lines2 = await readLinesAsync(fd2, buf2, bufferSize, nextPosition2)
if (lines1.length === 0 && lines2.length === 0) {
// End of file reached
return true
}
var equalLines = compareLines(lines1, lines2, options)
if (equalLines === 0) {
return false
}
nextPosition1 += calculateSize(lines1, equalLines)
nextPosition2 += calculateSize(lines2, equalLines)
}
} finally {
bufferPool.freeBuffers(bufferPair)
await closeFilesAsync(fd1, fd2, fdQueue)
}
}
/**
* Read lines from file starting with nextPosition.
* Returns 0 lines if eof is reached, otherwise returns at least one complete line.
*/
function readLinesSync(fd, buf, bufferSize, nextPosition) {
var lines = []
var chunk = ""
while (true) {
var size = fs.readSync(fd, buf, 0, bufferSize, nextPosition)
if (size === 0) {
// end of file
normalizeLastFileLine(lines)
return lines
}
chunk += buf.toString('utf8', 0, size)
lines = chunk.match(LINE_TOKENIZER_REGEXP)
if (lines.length > 1) {
return removeLastIncompleteLine(lines)
}
nextPosition += size
}
}
/**
* Read lines from file starting with nextPosition.
* Returns 0 lines if eof is reached, otherwise returns at least one complete line.
*/
async function readLinesAsync(fd, buf, bufferSize, nextPosition) {
var lines = []
var chunk = ""
while (true) {
var size = await fsPromise.read(fd, buf, 0, bufferSize, nextPosition)
if (size === 0) {
// end of file
normalizeLastFileLine(lines)
return lines
}
chunk += buf.toString('utf8', 0, size)
lines = chunk.match(LINE_TOKENIZER_REGEXP)
if (lines.length > 1) {
return removeLastIncompleteLine(lines)
}
nextPosition += size
}
}
function removeLastIncompleteLine(lines) {
const lastLine = lines[lines.length - 1]
if (!lastLine.endsWith('\n')) {
return lines.slice(0, lines.length - 1)
}
return lines
}
function normalizeLastFileLine(lines) {
if (lines.length === 0) {
return
}
const lastLine = lines[lines.length - 1]
if (!lastLine.endsWith('\n')) {
lines[lines.length - 1] = lastLine + '\n'
}
}
function calculateSize(lines, numberOfLines) {
var size = 0
for (var i = 0; i < numberOfLines; i++) {
var line = lines[i]
size += line.length
}
return size
}
function compareLines(lines1, lines2, options) {
var equalLines = 0
var len = lines1.length < lines2.length ? lines1.length : lines2.length
for (var i = 0; i < len; i++) {
var line1 = lines1[i]
var line2 = lines2[i]
if (options.ignoreLineEnding) {
line1 = trimLineEnding(line1)
line2 = trimLineEnding(line2)
}
if (options.ignoreWhiteSpaces) {
line1 = trimSpaces(line1)
line2 = trimSpaces(line2)
}
if (line1 !== line2) {
return equalLines
}
equalLines++
}
return equalLines
}
// Trims string like ' abc \n' into 'abc\n'
function trimSpaces(s) {
var matchResult = s.match(SPLIT_CONTENT_AND_LINE_ENDING_REGEXP);
var content = matchResult[1]
var lineEnding = matchResult[2]
var trimmed = content.replace(TRIM_WHITE_SPACES_REGEXP, '')
return trimmed + lineEnding
}
// Trims string like 'abc\r\n' into 'abc\n'
function trimLineEnding(s) {
return s.replace(TRIM_LINE_ENDING_REGEXP, '\n')
}
module.exports = {
compareSync: compareSync,
compareAsync: compareAsync
}

46
electron/node_modules/dir-compare/src/fs/BufferPool.js generated vendored Normal file
View file

@ -0,0 +1,46 @@
/**
* Collection of buffers to be shared between async processes.
* Avoids allocating buffers each time async process starts.
* bufSize - size of each buffer
* bufNo - number of buffers
* Caller has to make sure no more than bufNo async processes run simultaneously.
*/
function BufferPool(bufSize, bufNo) {
var bufferPool = []
for (var i = 0; i < bufNo; i++) {
bufferPool.push({
buf1: alloc(bufSize),
buf2: alloc(bufSize),
busy: false
})
}
var allocateBuffers = function () {
for (var j = 0; j < bufNo; j++) {
var bufferPair = bufferPool[j]
if (!bufferPair.busy) {
bufferPair.busy = true
return bufferPair
}
}
throw new Error('Async buffer limit reached')
}
return {
allocateBuffers: allocateBuffers,
freeBuffers: freeBuffers
}
function freeBuffers(bufferPair) {
bufferPair.busy = false
}
}
function alloc(bufSize) {
if (Buffer.alloc) {
return Buffer.alloc(bufSize)
}
return new Buffer(bufSize)
}
module.exports = BufferPool

View file

@ -0,0 +1,78 @@
'use strict'
var fs = require('fs')
var Queue = require('./Queue')
/**
* Limits the number of concurrent file handlers.
* Use it as a wrapper over fs.open() and fs.close().
* Example:
* var fdQueue = new FileDescriptorQueue(8)
* fdQueue.open(path, flags, (err, fd) =>{
* ...
* fdQueue.close(fd, (err) =>{
* ...
* })
* })
* As of node v7, calling fd.close without a callback is deprecated.
*/
var FileDescriptorQueue = function (maxFilesNo) {
var pendingJobs = new Queue()
var activeCount = 0
var open = function (path, flags, callback) {
pendingJobs.enqueue({
path: path,
flags: flags,
callback: callback
})
process()
}
var process = function () {
if (pendingJobs.getLength() > 0 && activeCount < maxFilesNo) {
var job = pendingJobs.dequeue()
activeCount++
fs.open(job.path, job.flags, job.callback)
}
}
var close = function (fd, callback) {
activeCount--
fs.close(fd, callback)
process()
}
var promises = {
open: function (path, flags) {
return new Promise(function (resolve, reject) {
open(path, flags, function (err, fd) {
if (err) {
reject(err)
} else {
resolve(fd)
}
})
})
},
close: function (fd) {
return new Promise(function (resolve, reject) {
close(fd, function (err) {
if (err) {
reject(err)
} else {
resolve()
}
})
})
}
}
return {
open: open,
close: close,
promises: promises
}
}
module.exports = FileDescriptorQueue

63
electron/node_modules/dir-compare/src/fs/Queue.js generated vendored Normal file
View file

@ -0,0 +1,63 @@
/*
Queue.js
A function to represent a queue
Created by Kate Morley - http://code.iamkate.com/ - and released under the terms
of the CC0 1.0 Universal legal code:
http://creativecommons.org/publicdomain/zero/1.0/legalcode
*/
var MAX_UNUSED_ARRAY_SIZE = 10000
/* Creates a new queue. A queue is a first-in-first-out (FIFO) data structure -
* items are added to the end of the queue and removed from the front.
*/
function Queue() {
// initialise the queue and offset
var queue = []
var offset = 0
// Returns the length of the queue.
this.getLength = function () {
return (queue.length - offset)
}
/* Enqueues the specified item. The parameter is:
*
* item - the item to enqueue
*/
this.enqueue = function (item) {
queue.push(item)
}
/* Dequeues an item and returns it. If the queue is empty, the value
* 'undefined' is returned.
*/
this.dequeue = function () {
// if the queue is empty, return immediately
if (queue.length === 0) {
return undefined
}
// store the item at the front of the queue
var item = queue[offset]
// increment the offset and remove the free space if necessary
if (++offset > MAX_UNUSED_ARRAY_SIZE) {
queue = queue.slice(offset)
offset = 0
}
// return the dequeued item
return item
}
}
module.exports = Queue

26
electron/node_modules/dir-compare/src/fs/fsPromise.js generated vendored Normal file
View file

@ -0,0 +1,26 @@
var fs = require('fs')
module.exports = {
readdir: function (path) {
return new Promise(function (resolve, reject) {
fs.readdir(path, function (err, files) {
if (err) {
reject(err)
} else {
resolve(files)
}
})
})
},
read: function (fd, buffer, offset, length, position) {
return new Promise(function (resolve, reject) {
fs.read(fd, buffer, offset, length, position, function(err, bytesRead) {
if(err){
reject(err)
} else {
resolve(bytesRead)
}
})
})
},
}

476
electron/node_modules/dir-compare/src/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,476 @@
/// <reference types="node" />
import * as fs from "fs"
/**
* Synchronously compares given paths.
* @param path1 Left file or directory to be compared.
* @param path2 Right file or directory to be compared.
* @param options Comparison options.
*/
export function compareSync(path1: string, path2: string, options?: Options): Result
/**
* Asynchronously compares given paths.
* @param path1 Left file or directory to be compared.
* @param path2 Right file or directory to be compared.
* @param options Comparison options.
*/
export function compare(path1: string, path2: string, options?: Options): Promise<Result>
/**
* Comparison options.
*/
export interface Options {
/**
* Properties to be used in various extension points ie. result builder.
*/
[key: string]: any
/**
* Compares files by size. Defaults to 'false'.
*/
compareSize?: boolean
/**
* Compares files by date of modification (stat.mtime). Defaults to 'false'.
*/
compareDate?: boolean
/**
* Two files are considered to have the same date if the difference between their modification dates fits within date tolerance. Defaults to 1000 ms.
*/
dateTolerance?: number
/**
* Compares files by content. Defaults to 'false'.
*/
compareContent?: boolean
/**
* Compares entries by symlink. Defaults to 'false'.
*/
compareSymlink?: boolean
/**
* Skips sub directories. Defaults to 'false'.
*/
skipSubdirs?: boolean
/**
* Ignore symbolic links. Defaults to 'false'.
*/
skipSymlinks?: boolean
/**
* Ignores case when comparing names. Defaults to 'false'.
*/
ignoreCase?: boolean
/**
* Toggles presence of diffSet in output. If true, only statistics are provided. Use this when comparing large number of files to avoid out of memory situations. Defaults to 'false'.
*/
noDiffSet?: boolean
/**
* File name filter. Comma separated minimatch patterns. See [Glob patterns](https://github.com/gliviu/dir-compare#glob-patterns).
*/
includeFilter?: string
/**
* File/directory name exclude filter. Comma separated minimatch patterns. See [Glob patterns](https://github.com/gliviu/dir-compare#glob-patterns)
*/
excludeFilter?: string
/**
* Callback for constructing result. Called for each compared entry pair.
*
* Updates 'statistics' and 'diffSet'.
*
* See [Custom result builder](https://github.com/gliviu/dir-compare#custom-result-builder).
*/
resultBuilder?: ResultBuilder
/**
* File comparison handler. See [Custom file comparators](https://github.com/gliviu/dir-compare#custom-file-content-comparators).
*/
compareFileSync?: CompareFileSync
/**
* File comparison handler. See [Custom file comparators](https://github.com/gliviu/dir-compare#custom-file-content-comparators).
*/
compareFileAsync?: CompareFileAsync
/**
* Entry name comparison handler. See [Custom name comparators](https://github.com/gliviu/dir-compare#custom-name-comparators).
*/
compareNameHandler?: CompareNameHandler
}
/**
* Callback for constructing result. Called for each compared entry pair.
*
* Updates 'statistics' and 'diffSet'.
*/
export type ResultBuilder =
/**
* @param entry1 Left entry.
* @param entry2 Right entry.
* @param state See [[DifferenceState]].
* @param level Depth level relative to root dir.
* @param relativePath Path relative to root dir.
* @param statistics Statistics to be updated.
* @param diffSet Status per each entry to be appended.
* Do not append if [[Options.noDiffSet]] is false.
* @param reason See [[Reason]]. Not available if entries are equal.
*/
(
entry1: Entry | undefined,
entry2: Entry | undefined,
state: DifferenceState,
level: number,
relativePath: string,
options: Options,
statistics: Statistics,
diffSet: Array<Difference> | undefined,
reason: Reason | undefined
) => void
export interface Entry {
name: string
absolutePath: string
path: string
stat: fs.Stats
lstat: fs.Stats
symlink: boolean
}
/**
* Comparison result.
*/
export interface Result extends Statistics {
/**
* List of changes (present if [[Options.noDiffSet]] is false).
*/
diffSet?: Array<Difference>
}
export interface Statistics {
/**
* Any property is allowed if default result builder is not used.
*/
[key: string]: any
/**
* True if directories are identical.
*/
same: boolean
/**
* Number of distinct entries.
*/
distinct: number
/**
* Number of equal entries.
*/
equal: number
/**
* Number of entries only in path1.
*/
left: number
/**
* Number of entries only in path2.
*/
right: number
/**
* Total number of differences (distinct+left+right).
*/
differences: number
/**
* Total number of entries (differences+equal).
*/
total: number
/**
* Number of distinct files.
*/
distinctFiles: number
/**
* Number of equal files.
*/
equalFiles: number
/**
* Number of files only in path1.
*/
leftFiles: number
/**
* Number of files only in path2
*/
rightFiles: number
/**
* Total number of different files (distinctFiles+leftFiles+rightFiles).
*/
differencesFiles: number
/**
* Total number of files (differencesFiles+equalFiles).
*/
totalFiles: number
/**
* Number of distinct directories.
*/
distinctDirs: number
/**
* Number of equal directories.
*/
equalDirs: number
/**
* Number of directories only in path1.
*/
leftDirs: number
/**
* Number of directories only in path2.
*/
rightDirs: number
/**
* Total number of different directories (distinctDirs+leftDirs+rightDirs).
*/
differencesDirs: number
/**
* Total number of directories (differencesDirs+equalDirs).
*/
totalDirs: number
/**
* Stats about broken links.
*/
brokenLinks: BrokenLinksStatistics
/**
* Statistics available if 'compareSymlink' options is used.
*/
symlinks?: SymlinkStatistics
}
export interface BrokenLinksStatistics {
/**
* Number of broken links only in path1
*/
leftBrokenLinks: number
/**
* Number of broken links only in path2
*/
rightBrokenLinks: number
/**
* Number of broken links with same name appearing in both path1 and path2 (leftBrokenLinks+rightBrokenLinks+distinctBrokenLinks)
*/
distinctBrokenLinks: number
/**
* Total number of broken links
*/
totalBrokenLinks: number
}
export interface SymlinkStatistics {
/**
* Number of distinct links.
*/
distinctSymlinks: number
/**
* Number of equal links.
*/
equalSymlinks: number
/**
* Number of links only in path1.
*/
leftSymlinks: number
/**
* Number of links only in path2
*/
rightSymlinks: number
/**
* Total number of different links (distinctSymlinks+leftSymlinks+rightSymlinks).
*/
differencesSymlinks: number
/**
* Total number of links (differencesSymlinks+equalSymlinks).
*/
totalSymlinks: number
}
/**
* State of left/right entries relative to each other.
*/
export type DifferenceState = "equal" | "left" | "right" | "distinct"
/**
* Type of entry.
*/
export type DifferenceType = "missing" | "file" | "directory" | "broken-link"
/**
* Provides reason when two identically named entries are distinct.
*/
export type Reason = "different-size" | "different-date" | "different-content" | "broken-link" | 'different-symlink'
export interface Difference {
/**
* Any property is allowed if default result builder is not used.
*/
[key: string]: any
/**
* Path not including file/directory name; can be relative or absolute depending on call to compare().
*/
path1?: string
/**
* Path not including file/directory name; can be relative or absolute depending on call to compare().
*/
path2?: string
/**
* Path relative to root dir.
*/
relativePath: string
/**
* Left file/directory name.
*/
name1?: string
/**
* Right file/directory name.
*/
name2?: string
/**
* See [[DifferenceState]]
*/
state: DifferenceState
/**
* Type of left entry.
*/
type1: DifferenceType
/**
* Type of right entry.
*/
type2: DifferenceType
/**
* Left file size.
*/
size1?: number
/**
* Right file size.
*/
size2?: number
/**
* Left entry modification date (stat.mtime).
*/
date1?: number
/**
* Right entry modification date (stat.mtime).
*/
date2?: number
/**
* Depth level relative to root dir.
*/
level: number
/**
* See [[Reason]].
* Not available if entries are equal.
*/
reason?: Reason
}
/**
* Synchronous file content comparison handler.
*/
export type CompareFileSync = (
path1: string,
stat1: fs.Stats,
path2: string,
stat2: fs.Stats,
options: Options
) => boolean
/**
* Asynchronous file content comparison handler.
*/
export type CompareFileAsync = (
path1: string,
stat1: fs.Stats,
path2: string,
stat2: fs.Stats,
options: Options
) => Promise<boolean>
export interface CompareFileHandler {
compareSync: CompareFileSync,
compareAsync: CompareFileAsync
}
/**
* Available file content comparison handlers.
* These handlers are used when [[Options.compareContent]] is set.
*/
export const fileCompareHandlers: {
/**
* Default file content comparison handlers, used if [[Options.compareFileAsync]] or [[Options.compareFileSync]] are not specified.
*
* Performs binary comparison.
*/
defaultFileCompare: CompareFileHandler,
/**
* Compares files line by line.
*
* Options:
* * ignoreLineEnding - tru/false (default: false)
* * ignoreWhiteSpaces - tru/false (default: false)
*/
lineBasedFileCompare: CompareFileHandler
}
/**
* Compares the names of two entries.
* The comparison should be dependent on received options (ie. case sensitive, ...).
* Returns 0 if names are identical, -1 if name1<name2, 1 if name1>name2.
*/
export type CompareNameHandler = (
name1: string,
name2: string,
options: Options
) => 0 | 1 | -1

204
electron/node_modules/dir-compare/src/index.js generated vendored Normal file
View file

@ -0,0 +1,204 @@
var util = require('util')
var pathUtils = require('path')
var fs = require('fs')
var compareSyncInternal = require('./compareSync')
var compareAsyncInternal = require('./compareAsync')
var defaultResultBuilderCallback = require('./resultBuilder/defaultResultBuilderCallback')
var defaultFileCompare = require('./fileCompareHandler/defaultFileCompare')
var lineBasedFileCompare = require('./fileCompareHandler/lineBasedFileCompare')
var defaultNameCompare = require('./nameCompare/defaultNameCompare')
var entryBuilder = require('./entry/entryBuilder')
var statsLifecycle = require('./statistics/statisticsLifecycle')
var loopDetector = require('./symlink/loopDetector')
var ROOT_PATH = pathUtils.sep
var compareSync = function (path1, path2, options) {
'use strict'
// realpathSync() is necessary for loop detection to work properly
var absolutePath1 = pathUtils.normalize(pathUtils.resolve(fs.realpathSync(path1)))
var absolutePath2 = pathUtils.normalize(pathUtils.resolve(fs.realpathSync(path2)))
var diffSet
options = prepareOptions(options)
if (!options.noDiffSet) {
diffSet = []
}
var statistics = statsLifecycle.initStats(options)
compareSyncInternal(
entryBuilder.buildEntry(absolutePath1, path1, pathUtils.basename(absolutePath1)),
entryBuilder.buildEntry(absolutePath2, path2, pathUtils.basename(absolutePath2)),
0, ROOT_PATH, options, statistics, diffSet, loopDetector.initSymlinkCache())
statsLifecycle.completeStatistics(statistics, options)
statistics.diffSet = diffSet
return statistics
}
var wrapper = {
realPath: function(path, options) {
return new Promise(function (resolve, reject) {
fs.realpath(path, options, function(err, resolvedPath) {
if(err){
reject(err)
} else {
resolve(resolvedPath)
}
})
})
}
}
var compareAsync = function (path1, path2, options) {
'use strict'
var absolutePath1, absolutePath2
return Promise.resolve()
.then(function () {
return Promise.all([wrapper.realPath(path1), wrapper.realPath(path2)])
})
.then(function (realPaths) {
var realPath1 = realPaths[0]
var realPath2 = realPaths[1]
// realpath() is necessary for loop detection to work properly
absolutePath1 = pathUtils.normalize(pathUtils.resolve(realPath1))
absolutePath2 = pathUtils.normalize(pathUtils.resolve(realPath2))
})
.then(function () {
options = prepareOptions(options)
var asyncDiffSet
if (!options.noDiffSet) {
asyncDiffSet = []
}
var statistics = statsLifecycle.initStats(options)
return compareAsyncInternal(
entryBuilder.buildEntry(absolutePath1, path1, pathUtils.basename(path1)),
entryBuilder.buildEntry(absolutePath2, path2, pathUtils.basename(path2)),
0, ROOT_PATH, options, statistics, asyncDiffSet, loopDetector.initSymlinkCache()).then(
function () {
statsLifecycle.completeStatistics(statistics, options)
if (!options.noDiffSet) {
var diffSet = []
rebuildAsyncDiffSet(statistics, asyncDiffSet, diffSet)
statistics.diffSet = diffSet
}
return statistics
})
})
}
var prepareOptions = function (options) {
options = options || {}
var clone = JSON.parse(JSON.stringify(options))
clone.resultBuilder = options.resultBuilder
clone.compareFileSync = options.compareFileSync
clone.compareFileAsync = options.compareFileAsync
clone.compareNameHandler = options.compareNameHandler
if (!clone.resultBuilder) {
clone.resultBuilder = defaultResultBuilderCallback
}
if (!clone.compareFileSync) {
clone.compareFileSync = defaultFileCompare.compareSync
}
if (!clone.compareFileAsync) {
clone.compareFileAsync = defaultFileCompare.compareAsync
}
if(!clone.compareNameHandler) {
clone.compareNameHandler = defaultNameCompare
}
clone.dateTolerance = clone.dateTolerance || 1000
clone.dateTolerance = Number(clone.dateTolerance)
if (isNaN(clone.dateTolerance)) {
throw new Error('Date tolerance is not a number')
}
return clone
}
// Async diffsets are kept into recursive structures.
// This method transforms them into one dimensional arrays.
var rebuildAsyncDiffSet = function (statistics, asyncDiffSet, diffSet) {
asyncDiffSet.forEach(function (rawDiff) {
if (!Array.isArray(rawDiff)) {
diffSet.push(rawDiff)
} else {
rebuildAsyncDiffSet(statistics, rawDiff, diffSet)
}
})
}
/**
* Options:
* compareSize: true/false - Compares files by size. Defaults to 'false'.
* compareDate: true/false - Compares files by date of modification (stat.mtime). Defaults to 'false'.
* dateTolerance: milliseconds - Two files are considered to have the same date if the difference between their modification dates fits within date tolerance. Defaults to 1000 ms.
* compareContent: true/false - Compares files by content. Defaults to 'false'.
* compareSymlink: true/false - Compares entries by symlink. Defaults to 'false'.
* skipSubdirs: true/false - Skips sub directories. Defaults to 'false'.
* skipSymlinks: true/false - Skips symbolic links. Defaults to 'false'.
* ignoreCase: true/false - Ignores case when comparing names. Defaults to 'false'.
* noDiffSet: true/false - Toggles presence of diffSet in output. If true, only statistics are provided. Use this when comparing large number of files to avoid out of memory situations. Defaults to 'false'.
* includeFilter: File name filter. Comma separated [minimatch](https://www.npmjs.com/package/minimatch) patterns.
* excludeFilter: File/directory name exclude filter. Comma separated [minimatch](https://www.npmjs.com/package/minimatch) patterns.
* resultBuilder: Callback for constructing result.
* function (entry1, entry2, state, level, relativePath, options, statistics, diffSet). Called for each compared entry pair. Updates 'statistics' and 'diffSet'.
* compareFileSync, compareFileAsync: Callbacks for file comparison.
* compareNameHandler: Callback for name comparison
*
* Result:
* same: true if directories are identical
* distinct: number of distinct entries
* equal: number of equal entries
* left: number of entries only in path1
* right: number of entries only in path2
* differences: total number of differences (distinct+left+right)
* total: total number of entries (differences+equal)
* distinctFiles: number of distinct files
* equalFiles: number of equal files
* leftFiles: number of files only in path1
* rightFiles: number of files only in path2
* differencesFiles: total number of different files (distinctFiles+leftFiles+rightFiles)
* totalFiles: total number of files (differencesFiles+equalFiles)
* distinctDirs: number of distinct directories
* equalDirs: number of equal directories
* leftDirs: number of directories only in path1
* rightDirs: number of directories only in path2
* differencesDirs: total number of different directories (distinctDirs+leftDirs+rightDirs)
* totalDirs: total number of directories (differencesDirs+equalDirs)
* brokenLinks:
* leftBrokenLinks: number of broken links only in path1
* rightBrokenLinks: number of broken links only in path2
* distinctBrokenLinks: number of broken links with same name appearing in both path1 and path2
* totalBrokenLinks: total number of broken links (leftBrokenLinks+rightBrokenLinks+distinctBrokenLinks)
* symlinks: Statistics available if 'compareSymlink' options is used
* distinctSymlinks: number of distinct links
* equalSymlinks: number of equal links
* leftSymlinks: number of links only in path1
* rightSymlinks: number of links only in path2
* differencesSymlinks: total number of different links (distinctSymlinks+leftSymlinks+rightSymlinks)
* totalSymlinks: total number of links (differencesSymlinks+equalSymlinks)
* diffSet - List of changes (present if Options.noDiffSet is false)
* path1: absolute path not including file/directory name,
* path2: absolute path not including file/directory name,
* relativePath: common path relative to root,
* name1: file/directory name
* name2: file/directory name
* state: one of equal, left, right, distinct,
* type1: one of missing, file, directory, broken-link
* type2: one of missing, file, directory, broken-link
* size1: file size
* size2: file size
* date1: modification date (stat.mtime)
* date2: modification date (stat.mtime)
* level: depth
* reason: Provides reason when two identically named entries are distinct
* Not available if entries are equal
* One of "different-size", "different-date", "different-content", "broken-link", "different-symlink"
*/
module.exports = {
compareSync: compareSync,
compare: compareAsync,
fileCompareHandlers: {
defaultFileCompare: defaultFileCompare,
lineBasedFileCompare: lineBasedFileCompare
}
}

View file

@ -0,0 +1,12 @@
module.exports = function compareName(name1, name2, options) {
if (options.ignoreCase) {
name1 = name1.toLowerCase()
name2 = name2.toLowerCase()
}
return strcmp(name1, name2)
}
function strcmp(str1, str2) {
return ((str1 === str2) ? 0 : ((str1 > str2) ? 1 : -1))
}

View file

@ -0,0 +1,27 @@
'use strict'
var pathUtils = require('path')
var common = require('../entry/entryBuilder')
var entryType = require('../entry/entryType')
module.exports = function (entry1, entry2, state, level, relativePath, options, statistics, diffSet, reason) {
if (options.noDiffSet) {
return
}
diffSet.push({
path1: entry1 ? pathUtils.dirname(entry1.path) : undefined,
path2: entry2 ? pathUtils.dirname(entry2.path) : undefined,
relativePath: relativePath,
name1: entry1 ? entry1.name : undefined,
name2: entry2 ? entry2.name : undefined,
state: state,
type1: entryType.getType(entry1),
type2: entryType.getType(entry2),
level: level,
size1: entry1 ? entry1.stat.size : undefined,
size2: entry2 ? entry2.stat.size : undefined,
date1: entry1 ? entry1.stat.mtime : undefined,
date2: entry2 ? entry2.stat.mtime : undefined,
reason: reason
})
}

View file

@ -0,0 +1,59 @@
/**
* Controls creation/completion of global statistics object.
*/
module.exports = {
initStats(options) {
var symlinkStatistics = undefined
if (options.compareSymlink) {
symlinkStatistics = {
distinctSymlinks: 0,
equalSymlinks: 0,
leftSymlinks: 0,
rightSymlinks: 0,
differencesSymlinks: 0,
totalSymlinks: 0,
}
}
var brokenLinksStatistics = {
leftBrokenLinks: 0,
rightBrokenLinks: 0,
distinctBrokenLinks: 0,
}
return {
distinct: 0,
equal: 0,
left: 0,
right: 0,
distinctFiles: 0,
equalFiles: 0,
leftFiles: 0,
rightFiles: 0,
distinctDirs: 0,
equalDirs: 0,
leftDirs: 0,
rightDirs: 0,
brokenLinks: brokenLinksStatistics,
symlinks: symlinkStatistics,
same: undefined
}
},
completeStatistics(statistics, options) {
statistics.differences = statistics.distinct + statistics.left + statistics.right
statistics.differencesFiles = statistics.distinctFiles + statistics.leftFiles + statistics.rightFiles
statistics.differencesDirs = statistics.distinctDirs + statistics.leftDirs + statistics.rightDirs
statistics.total = statistics.equal + statistics.differences
statistics.totalFiles = statistics.equalFiles + statistics.differencesFiles
statistics.totalDirs = statistics.equalDirs + statistics.differencesDirs
var brokenLInksStats = statistics.brokenLinks
brokenLInksStats.totalBrokenLinks = brokenLInksStats.leftBrokenLinks + brokenLInksStats.rightBrokenLinks + brokenLInksStats.distinctBrokenLinks
statistics.same = statistics.differences ? false : true
if (options.compareSymlink) {
statistics.symlinks.differencesSymlinks = statistics.symlinks.distinctSymlinks +
statistics.symlinks.leftSymlinks + statistics.symlinks.rightSymlinks
statistics.symlinks.totalSymlinks = statistics.symlinks.differencesSymlinks + statistics.symlinks.equalSymlinks
}
}
}

View file

@ -0,0 +1,62 @@
/**
* Calculates comparison statistics.
*/
module.exports = {
updateStatisticsBoth: function (entry1, entry2, same, reason, type, statistics, options) {
same ? statistics.equal++ : statistics.distinct++
if (type === 'file') {
same ? statistics.equalFiles++ : statistics.distinctFiles++
} else if (type === 'directory') {
same ? statistics.equalDirs++ : statistics.distinctDirs++
} else if (type === 'broken-link') {
statistics.brokenLinks.distinctBrokenLinks++
} else {
throw new Error('Unexpected type ' + type)
}
var isSymlink1 = entry1 ? entry1.isSymlink : false
var isSymlink2 = entry2 ? entry2.isSymlink : false
var isSymlink = isSymlink1 || isSymlink2
if (options.compareSymlink && isSymlink) {
var symlinks = statistics.symlinks
if (reason === 'different-symlink') {
symlinks.distinctSymlinks++
} else {
symlinks.equalSymlinks++
}
}
},
updateStatisticsLeft: function (entry1, type, statistics, options) {
statistics.left++
if (type === 'file') {
statistics.leftFiles++
} else if (type === 'directory') {
statistics.leftDirs++
} else if (type === 'broken-link') {
statistics.brokenLinks.leftBrokenLinks++
} else {
throw new Error('Unexpected type ' + type)
}
if (options.compareSymlink && entry1.isSymlink) {
statistics.symlinks.leftSymlinks++
}
},
updateStatisticsRight: function (entry2, type, statistics, options) {
statistics.right++
if (type === 'file') {
statistics.rightFiles++
} else if (type === 'directory') {
statistics.rightDirs++
} else if (type === 'broken-link') {
statistics.brokenLinks.rightBrokenLinks++
} else {
throw new Error('Unexpected type ' + type)
}
if (options.compareSymlink && entry2.isSymlink) {
statistics.symlinks.rightSymlinks++
}
},
}

View file

@ -0,0 +1,51 @@
var fs = require('fs')
/**
* Provides symlink loop detection to directory traversal algorithm.
*/
module.exports = {
detectLoop: function (entry, symlinkCache) {
if (entry && entry.isSymlink) {
var realPath = fs.realpathSync(entry.absolutePath)
if (symlinkCache[realPath]) {
return true
}
}
return false
},
initSymlinkCache: function() {
return {
dir1: {},
dir2: {}
}
},
updateSymlinkCache: function(symlinkCache, rootEntry1, rootEntry2, loopDetected1, loopDetected2) {
var symlinkCachePath1, symlinkCachePath2
if (rootEntry1 && !loopDetected1) {
symlinkCachePath1 = rootEntry1.isSymlink ? fs.realpathSync(rootEntry1.absolutePath) : rootEntry1.absolutePath
symlinkCache.dir1[symlinkCachePath1] = true
}
if (rootEntry2 && !loopDetected2) {
symlinkCachePath2 = rootEntry2.isSymlink ? fs.realpathSync(rootEntry2.absolutePath) : rootEntry2.absolutePath
symlinkCache.dir2[symlinkCachePath2] = true
}
},
cloneSymlinkCache: function (symlinkCache) {
return {
dir1: shallowClone(symlinkCache.dir1),
dir2: shallowClone(symlinkCache.dir2)
}
},
}
function shallowClone(obj) {
var cloned = {}
Object.keys(obj).forEach(function (key) {
cloned[key] = obj[key]
})
return cloned
}