Comparing version 2.2.19 to 3.0.0
413
index.js
@@ -1,160 +0,159 @@ | ||
'use strict'; | ||
'use strict' | ||
module.exports = ignore; | ||
ignore.Ignore = Ignore; | ||
module.exports = (options = {}) => new IgnoreBase(options) | ||
var EE = require('events').EventEmitter; | ||
var node_util = require('util'); | ||
var node_fs = require('fs'); | ||
var array_slice = Array.prototype.slice | ||
function ignore(options) { | ||
return new Ignore(options); | ||
function make_array (subject) { | ||
return Array.isArray(subject) | ||
? subject | ||
: [subject] | ||
} | ||
var exists = node_fs.existsSync | ||
? function(file) { | ||
return node_fs.existsSync(file); | ||
} | ||
// if node <= 0.6, there's no fs.existsSync method. | ||
: function(file) { | ||
try { | ||
node_fs.statSync(file); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
}; | ||
var REGEX_BLANK_LINE = /^\s+$/ | ||
var REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\\!/ | ||
var REGEX_LEADING_EXCAPED_HASH = /^\\#/ | ||
var SLASH = '/' | ||
// Select the first existing file of the file list | ||
ignore.select = function(files) { | ||
var selected; | ||
files.some(function(file) { | ||
if (exists(file)) { | ||
selected = file; | ||
return true; | ||
} | ||
}); | ||
class IgnoreBase { | ||
constructor () { | ||
this._patterns = [] | ||
this._rules = [] | ||
this._files = [] | ||
return selected; | ||
}; | ||
this._initCache() | ||
} | ||
// @param {Object} options | ||
// - ignore: {Array} | ||
// - twoGlobstars: {boolean=false} enable pattern `'**'` (two consecutive asterisks), default to `false`. | ||
// If false, ignore patterns with two globstars will be omitted | ||
// - matchCase: {boolean=} case sensitive. | ||
// By default, git is case-insensitive | ||
function Ignore(options) { | ||
options = options || {}; | ||
_initCache () { | ||
this._cache = {} | ||
} | ||
this.options = options; | ||
this._patterns = []; | ||
this._rules = []; | ||
this._ignoreFiles = []; | ||
// @param {Array.<string>|string} pattern | ||
add (pattern) { | ||
this._added = false | ||
options.ignore = options.ignore || [ | ||
// Some files or directories which we should ignore for most cases. | ||
'.git', | ||
'.svn', | ||
'.DS_Store' | ||
]; | ||
if (typeof pattern === 'string') { | ||
pattern = pattern.split('\r?\n') | ||
} | ||
this.addPattern(options.ignore); | ||
} | ||
make_array(pattern).forEach(this._addPattern, this) | ||
// Events: | ||
// 'warn': , | ||
// will warn when encounter '`**`' (two consecutive asterisks) | ||
// which is not compatible with all platforms (not works on Mac OS for example) | ||
node_util.inherits(Ignore, EE); | ||
// Some rules have just added to the ignore, | ||
// making the behavior changed. | ||
if (this._added) { | ||
this._initCache() | ||
} | ||
function makeArray(subject) { | ||
return Array.isArray(subject) | ||
? subject | ||
: subject === undefined || subject === null | ||
? [] | ||
: [subject]; | ||
} | ||
return this | ||
} | ||
// legacy | ||
addPattern (pattern) { | ||
return this.add(pattern) | ||
} | ||
// @param {Array.<string>|string} pattern | ||
Ignore.prototype.addPattern = function(pattern) { | ||
makeArray(pattern).forEach(this._addPattern, this); | ||
return this; | ||
}; | ||
_addPattern (pattern) { | ||
if (this._checkPattern(pattern)) { | ||
var rule = this._createRule(pattern) | ||
this._added = true | ||
this._rules.push(rule) | ||
} | ||
} | ||
_checkPattern (pattern) { | ||
// > A blank line matches no files, so it can serve as a separator for readability. | ||
return pattern | ||
&& typeof pattern === 'string' | ||
&& !REGEX_BLANK_LINE.test(pattern) | ||
Ignore.prototype._addPattern = function(pattern) { | ||
if (this._simpleTest(pattern)) { | ||
var rule = this._createRule(pattern); | ||
this._rules.push(rule); | ||
// > A line starting with # serves as a comment. | ||
&& pattern.indexOf('#') !== 0 | ||
} | ||
}; | ||
filter (paths) { | ||
return make_array(paths).filter(path => this._filter(path)) | ||
} | ||
Ignore.prototype.filter = function(paths) { | ||
return paths.filter(this._filter, this); | ||
}; | ||
createFilter () { | ||
return path => this._filter(path) | ||
} | ||
_createRule (pattern) { | ||
var rule_object = { | ||
origin: pattern | ||
} | ||
Ignore.prototype._simpleTest = function(pattern) { | ||
// Whitespace dirs are allowed, so only filter blank pattern. | ||
var pass = pattern | ||
// And not start with a '#' | ||
&& pattern.indexOf('#') !== 0 | ||
&& !~this._patterns.indexOf(pattern); | ||
if (pattern.indexOf('!') === 0) { | ||
rule_object.negative = true | ||
pattern = pattern.substr(1) | ||
} | ||
this._patterns.push(pattern); | ||
pattern = pattern | ||
.replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!') | ||
.replace(REGEX_LEADING_EXCAPED_HASH, '#') | ||
if (~pattern.indexOf('**')) { | ||
this.emit('warn', { | ||
code: 'WGLOBSTARS', | ||
data: { | ||
origin: pattern | ||
}, | ||
message: '`**` found, which is not compatible cross all platforms.' | ||
}); | ||
rule_object.pattern = pattern | ||
rule_object.regex = regex(pattern) | ||
if (!this.options.twoGlobstars) { | ||
return false; | ||
} | ||
return rule_object | ||
} | ||
return pass; | ||
}; | ||
_filter (path, slices) { | ||
if (!path) { | ||
return false | ||
} | ||
var REGEX_LEADING_EXCLAMATION = /^\\\!/; | ||
var REGEX_LEADING_HASH = /^\\#/; | ||
if (path in this._cache) { | ||
return this._cache[path] | ||
} | ||
Ignore.prototype._createRule = function(pattern) { | ||
var rule_object = { | ||
origin: pattern | ||
}; | ||
if (!slices) { | ||
// path/to/a.js | ||
// ['path', 'to', 'a.js'] | ||
slices = path.split(SLASH) | ||
var match_start; | ||
// '/b/a.js' -> ['', 'b', 'a.js'] -> [''] | ||
if (slices.length && !slices[0]) { | ||
slices = slices.slice(1) | ||
slices[0] = SLASH + slices[0] | ||
} | ||
} | ||
if (pattern.indexOf('!') === 0) { | ||
rule_object.negative = true; | ||
pattern = pattern.substr(1); | ||
slices.pop() | ||
return this._cache[path] = slices.length | ||
// > It is not possible to re-include a file if a parent directory of that file is excluded. | ||
// If the path contains a parent directory, check the parent first | ||
? this._filter(slices.join(SLASH) + SLASH, slices) | ||
&& this._test(path) | ||
// Or only test the path | ||
: this._test(path) | ||
} | ||
pattern = pattern | ||
.replace(REGEX_LEADING_EXCLAMATION, '!') | ||
.replace(REGEX_LEADING_HASH, '#'); | ||
_test (path) { | ||
var matched | ||
rule_object.pattern = pattern; | ||
this._rules.forEach(rule => { | ||
// if matched = true, then we only test negative rules | ||
// if matched = false, then we test non-negative rules | ||
if (!(matched ^ rule.negative)) { | ||
matched = rule.negative ^ rule.regex.test(path) | ||
} | ||
}) | ||
rule_object.regex = this.makeRegex(pattern); | ||
return !matched | ||
} | ||
} | ||
return rule_object; | ||
}; | ||
// > If the pattern ends with a slash, | ||
// > it is removed for the purpose of the following description, | ||
// > but it would only find a match with a directory. | ||
// > In other words, foo/ will match a directory foo and paths underneath it, | ||
// > but will not match a regular file or a symbolic link foo (this is consistent with the way how pathspec works in general in Git). | ||
// > If the pattern ends with a slash, | ||
// > it is removed for the purpose of the following description, | ||
// > but it would only find a match with a directory. | ||
// > In other words, foo/ will match a directory foo and paths underneath it, | ||
// > but will not match a regular file or a symbolic link foo | ||
// > (this is consistent with the way how pathspec works in general in Git). | ||
// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' | ||
@@ -167,18 +166,29 @@ // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call | ||
// Escape metacharacters | ||
// OOPS: Tested up from git 1.9.3 -> 2.6.4 | ||
// Trailing whitespaces are not ignored actually!!!! | ||
// > Trailing spaces are ignored unless they are quoted with backslash ("\") | ||
[ | ||
/\\\s/g, | ||
function(match) { | ||
return ' ' | ||
} | ||
], | ||
// Escape metacharacters | ||
// which is written down by users but means special for regular expressions. | ||
// > There are 12 characters with special meanings: | ||
// > - the backslash \, | ||
// > - the caret ^, | ||
// > - the dollar sign $, | ||
// > - the period or dot ., | ||
// > - the vertical bar or pipe symbol |, | ||
// > - the question mark ?, | ||
// > - the asterisk or star *, | ||
// > - the plus sign +, | ||
// > - the opening parenthesis (, | ||
// > - the closing parenthesis ), | ||
// > - and the opening square bracket [, | ||
// > - the opening curly brace {, | ||
// > There are 12 characters with special meanings: | ||
// > - the backslash \, | ||
// > - the caret ^, | ||
// > - the dollar sign $, | ||
// > - the period or dot ., | ||
// > - the vertical bar or pipe symbol |, | ||
// > - the question mark ?, | ||
// > - the asterisk or star *, | ||
// > - the plus sign +, | ||
// > - the opening parenthesis (, | ||
// > - the closing parenthesis ), | ||
// > - and the opening square bracket [, | ||
// > - the opening curly brace {, | ||
// > These special characters are often called "metacharacters". | ||
@@ -188,3 +198,3 @@ [ | ||
function(match) { | ||
return '\\' + match; | ||
return '\\' + match | ||
} | ||
@@ -196,18 +206,23 @@ ], | ||
// > A leading slash matches the beginning of the pathname. | ||
// > A leading slash matches the beginning of the pathname. | ||
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". | ||
// A leading slash matches the beginning of the pathname | ||
// A leading slash matches the beginning of the pathname | ||
/^\//, | ||
'^' | ||
function () { | ||
return '^' | ||
} | ||
], | ||
// replace special metacharacter slash after the leading slash | ||
[ | ||
/\//g, | ||
'\\/' | ||
function () { | ||
return '\\/' | ||
} | ||
], | ||
[ | ||
// > A leading "**" followed by a slash means match in all directories. | ||
// > For example, "**/foo" matches file or directory "foo" anywhere, | ||
// > the same as pattern "foo". | ||
// > A leading "**" followed by a slash means match in all directories. | ||
// > For example, "**/foo" matches file or directory "foo" anywhere, | ||
// > the same as pattern "foo". | ||
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly under directory "foo". | ||
@@ -219,3 +234,5 @@ // Notice that the '*'s have been replaced as '\\*' | ||
// just remove it | ||
'' | ||
function () { | ||
return '(?:.*\\/)?' | ||
} | ||
], | ||
@@ -238,2 +255,3 @@ | ||
// 'js' will not match 'js.' | ||
// 'ab' will not match 'abc' | ||
/(?:[^*\/])$/, | ||
@@ -244,3 +262,3 @@ function(match) { | ||
// 'js' will match 'a.js' and 'a.js/' | ||
return match + '(?=$|\\/)'; | ||
return match + '(?=$|\\/)' | ||
} | ||
@@ -254,3 +272,11 @@ ], | ||
/^(?=[^\^])/, | ||
'(?:^|\\/)' | ||
function (match) { | ||
return !/\/(?!$)/.test(this) | ||
// > If the pattern does not contain a slash /, Git treats it as a shell glob pattern | ||
// Actually, if there is only a trailing slash, git also treats it as a shell glob pattern | ||
? '(?:^|\\/)' | ||
// > Otherwise, Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) | ||
: '^' | ||
} | ||
], | ||
@@ -260,3 +286,3 @@ | ||
[ | ||
// > A slash followed by two consecutive asterisks then a slash matches zero or more directories. | ||
// > A slash followed by two consecutive asterisks then a slash matches zero or more directories. | ||
// > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. | ||
@@ -268,3 +294,5 @@ // '/**/' | ||
// should not use '*', or it will be replaced by the next replacer | ||
'(?:\\/[^\\/]+)*\\/' | ||
function () { | ||
return '(?:\\/[^\\/]+)*\\/' | ||
} | ||
], | ||
@@ -283,3 +311,3 @@ | ||
// '*.js' doesn't match 'abc' | ||
return p1 + '[^\\/]*'; | ||
return p1 + '[^\\/]*' | ||
} | ||
@@ -292,84 +320,31 @@ ], | ||
// simply remove it | ||
'' | ||
function () { | ||
return '' | ||
} | ||
], | ||
[ | ||
// escape back | ||
/\\\\\\/g, | ||
'\\' | ||
function () { | ||
return '\\' | ||
} | ||
] | ||
]; | ||
] | ||
var cache = {} | ||
// @param {pattern} | ||
Ignore.prototype.makeRegex = function(pattern) { | ||
var source = REPLACERS.reduce(function(prev, current) { | ||
return prev.replace(current[0], current[1]); | ||
}, pattern); | ||
return new RegExp(source, this.options.matchCase ? '' : 'i'); | ||
}; | ||
Ignore.prototype._filter = function(path) { | ||
var rules = this._rules; | ||
var i = 0; | ||
var length = rules.length; | ||
var matched; | ||
var rule; | ||
for (; i < length; i++) { | ||
rule = rules[i]; | ||
// if matched = true, then we only test negative rules | ||
// if matched = false, then we test non-negative rules | ||
if (!(matched ^ rule.negative)) { | ||
matched = rule.negative ^ rule.regex.test(path); | ||
} else { | ||
continue; | ||
} | ||
function regex (pattern) { | ||
var r = cache[pattern] | ||
if (r) { | ||
return r | ||
} | ||
return !matched; | ||
}; | ||
var source = REPLACERS.reduce((prev, current) => { | ||
return prev.replace(current[0], current[1].bind(pattern)) | ||
}, pattern) | ||
Ignore.prototype.createFilter = function() { | ||
var self = this; | ||
return function(path) { | ||
return self._filter(path); | ||
}; | ||
}; | ||
// @param {Array.<path>|path} a | ||
Ignore.prototype.addIgnoreFile = function(files) { | ||
makeArray(files).forEach(this._addIgnoreFile, this); | ||
return this; | ||
}; | ||
Ignore.prototype._addIgnoreFile = function(file) { | ||
if (this._checkRuleFile(file)) { | ||
this._ignoreFiles.push(file); | ||
var content; | ||
try { | ||
content = node_fs.readFileSync(file); | ||
} catch (e) {} | ||
if (content) { | ||
this.addPattern(content.toString().split(/\r?\n/)); | ||
} | ||
} | ||
}; | ||
Ignore.prototype._checkRuleFile = function(file) { | ||
return file !== '.' | ||
&& file !== '..' | ||
&& !~this._ignoreFiles.indexOf(file); | ||
}; | ||
return cache[pattern] = new RegExp(source, 'i') | ||
} |
{ | ||
"name": "ignore", | ||
"version": "2.2.19", | ||
"version": "3.0.0", | ||
"description": "Ignore is a manager and filter for .gitignore rules.", | ||
"main": "index.js", | ||
"main": "./ignore.js", | ||
"files": [ | ||
@@ -11,3 +11,3 @@ "index.js", | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha --reporter spec ./test/ignore.js" | ||
"test": "mocha --reporter spec ./test/ignore.js" | ||
}, | ||
@@ -28,2 +28,5 @@ "repository": { | ||
"regex", | ||
"fnmatch", | ||
"glob", | ||
"asterisks", | ||
"regular-expression" | ||
@@ -30,0 +33,0 @@ ], |
124
README.md
@@ -0,8 +1,6 @@ | ||
[![Build Status](https://travis-ci.org/kaelzhang/node-ignore.png?branch=master)](https://travis-ci.org/kaelzhang/node-ignore) | ||
[![npm module downloads per month](http://img.shields.io/npm/dm/ignore.svg)](https://www.npmjs.org/package/ignore) | ||
# ignore | ||
[![NPM version](https://badge.fury.io/js/ignore.png)](http://badge.fury.io/js/ignore) | ||
[![npm module downloads per month](http://img.shields.io/npm/dm/ignore.svg)](https://www.npmjs.org/package/ignore) | ||
[![Build Status](https://travis-ci.org/kaelzhang/node-ignore.png?branch=master)](https://travis-ci.org/kaelzhang/node-ignore) | ||
[![Dependency Status](https://gemnasium.com/kaelzhang/node-ignore.png)](https://gemnasium.com/kaelzhang/node-ignore) | ||
`ignore` is a manager and filter which implemented in pure JavaScript according to the .gitignore [spec](http://git-scm.com/docs/gitignore). | ||
@@ -12,11 +10,7 @@ | ||
## Installation | ||
npm install ignore --save | ||
## Usage | ||
```js | ||
var ignore = require('ignore'); | ||
var ig = ignore(options).addPattern(['.abc/*', '!.abc/d/']); | ||
var ignore = require('ignore') | ||
var ig = ignore().add(['.abc/*', '!.abc/d/']) | ||
``` | ||
@@ -28,7 +22,7 @@ | ||
var paths = [ | ||
'.abc/a.js', // filtered out | ||
'.abc/d/e.js' // included | ||
]; | ||
'.abc/a.js', // filtered out | ||
'.abc/d/e.js' // included | ||
] | ||
ig.filter(paths); // ['.abc/d/e.js'] | ||
ig.filter(paths) // ['.abc/d/e.js'] | ||
``` | ||
@@ -42,16 +36,2 @@ | ||
### With ignore files | ||
For most cases, we'd better use only one ignore file. We could use `ignore.select` to select the first existing file. | ||
```js | ||
ignore().addIgnoreFile( | ||
ignore.select([ | ||
'.xxxignore', | ||
'.gitignore', | ||
'.ignore' | ||
]) | ||
); | ||
``` | ||
## Why another ignore? | ||
@@ -70,32 +50,32 @@ | ||
- '`**/foo`' should match '`foo`' anywhere. | ||
- Prevent re-including a file if a parent directory of that file is excluded. | ||
## Methods | ||
### .addPattern(pattern) | ||
### .add(pattern) | ||
### .add(patterns) | ||
- pattern `String` Ignore pattern. | ||
- patterns `Array.<pattern>` Array of ignore patterns. | ||
Adds a rule or several rules to the current manager. | ||
#### Returns `this` | ||
Returns `this` | ||
#### pattern `String|Array.<String>` | ||
The ignore rule or a array of rules. | ||
Notice that a line starting with `'#'`(hash) is treated as a comment. Put a backslash (`'\'`) in front of the first hash for patterns that begin with a hash, if you want to ignore a file with a hash at the beginning of the filename. | ||
```js | ||
ignore().addPattern('#abc').filter(['#abc']); // ['#abc'] | ||
ignore().addPattern('\#abc').filter(['#abc']); // [] | ||
ignore().add('#abc').filter(['#abc']) // ['#abc'] | ||
ignore().add('\#abc').filter(['#abc']) // [] | ||
``` | ||
### .addIgnoreFile(path) | ||
<!-- ### .addIgnoreFile(path) | ||
Adds rules from a ignore file or several files | ||
Adds rules from a ignore file or several files | ||
#### Returns `this` | ||
#### Rule `String|Array.<String>` | ||
#### Rule `String|Array.<String>` --> | ||
@@ -107,6 +87,4 @@ | ||
#### paths `Array.<path>` | ||
- paths `Array.<path>` The array of paths to be filtered. | ||
The array of paths to be filtered. | ||
*NOTICE* that each `path` here should be a relative path to the root of your repository. Suppose the dir structure is: | ||
@@ -122,3 +100,3 @@ | ||
|-- .c | ||
|-- .DS_store | ||
|-- .DS_store | ||
``` | ||
@@ -136,16 +114,15 @@ | ||
Usually, you could use [`glob`](http://npmjs.org/package/glob) to fetch the structure of the current directory: | ||
Usually, you could use [`glob`](http://npmjs.org/package/glob) with `option.mark = true` to fetch the structure of the current directory: | ||
```js | ||
var glob = require('glob'); | ||
var glob = require('glob') | ||
glob('**', function(err, files){ | ||
var filtered; | ||
if ( err ) { | ||
console.log(err) | ||
return | ||
} | ||
if ( err ) { | ||
console.log(err); | ||
} else { | ||
filtered = ignore().addIgnoreFile('.gitignore').filter(files); | ||
console.log(filtered); | ||
} | ||
}); | ||
var filtered = ignore().add(patterns).filter(files) | ||
console.log(filtered) | ||
}) | ||
``` | ||
@@ -157,37 +134,2 @@ | ||
#### Returns `function(path)` | ||
The filter function. | ||
## Constructor: ignore.Ignore | ||
```js | ||
new ignore.Ignore(options); | ||
ignore(options); | ||
``` | ||
#### options.matchCase `boolean=false` | ||
By default, all ignore rules will be treated as case-insensitive ones as well as the git does. | ||
#### options.twoGlobstars `boolean=false` | ||
By defailt, `ignoreRules` will omit every pattern that includes '`**`' (two consecutive asterisks) which is not compatible cross operating systems, because the behavior of file .gitignore depends on the implementation of command `fnmatch` in shell. | ||
By the way, Mac OS doesn't support '`**`'. | ||
#### options.ignore `Array.<String>` | ||
The ignore rules to be added. Default to `['.git', '.svn', '.DS_Store']` | ||
If you want those directories to be included, you could | ||
```js | ||
ignore({ | ||
ignore: [] | ||
}); | ||
``` | ||
You can also use `.addPattern()` method to do this. | ||
Returns `function(path)` the filter function. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
22525
5
554
0
128
1