Comparing version 4.0.6 to 5.0.0
@@ -1,5 +0,21 @@ | ||
# `node-ignore` 4 ChangeLog | ||
# `node-ignore` 5 ChangeLog | ||
# 5.x | ||
## 2018-08-13, Version 5.0.0 | ||
- **SEMVER-MAJOR**: [#20](https://github.com/kaelzhang/node-ignore/issues/20): it will throw if an invalid pathname passes into `.ignores(pathname)`, see [Upgrade 4.x -> 5.x](https://github.com/kaelzhang/node-ignore#upgrade-4x---5x). | ||
- **FEATURE**: [#31](https://github.com/kaelzhang/node-ignore/issues/31): adds a new method [`.test(pathname)`](https://github.com/kaelzhang/node-ignore#testpathname-pathname-since-500). | ||
- **BENCHMARK**: improves performance by 26%. | ||
# 4.x | ||
## 2018-08-12, Version 4.0.6 | ||
- **PATCH**: `Object.prototype` methods will not ruin the result any more. | ||
## ~ 2018-08-09, Version 4.0.1 - 4.0.5 | ||
- **PATCH**: updates README.md about frequent asked quesions from github issues. | ||
## 2018-06-22, Version 4.0.0 | ||
@@ -12,9 +28,1 @@ | ||
- **PATCH**: fixes typescript declaration. | ||
## ~ 2018-08-09, Version 4.0.1 - 4.0.5 | ||
- **PATCH**: updates README.md about frequent asked quesions from github issues. | ||
## 2018-08-12, Version 4.0.6 | ||
- **PATCH**: `Object.prototype` methods will not ruin the result any more. |
@@ -0,1 +1,8 @@ | ||
type Pathname = string | ||
interface TestResult { | ||
ignored: boolean | ||
unignored: boolean | ||
} | ||
interface Ignore { | ||
@@ -7,3 +14,3 @@ /** | ||
*/ | ||
add(pattern: string | Ignore): Ignore | ||
add(pattern: string | Ignore): this | ||
/** | ||
@@ -14,3 +21,3 @@ * Adds several rules to the current manager. | ||
*/ | ||
add(patterns: (string | Ignore)[]): Ignore | ||
add(patterns: (string | Ignore)[]): this | ||
@@ -23,3 +30,3 @@ /** | ||
*/ | ||
filter(paths: string[]): string[] | ||
filter(pathnames: Pathname[]): Pathname[] | ||
/** | ||
@@ -29,3 +36,3 @@ * Creates a filter function which could filter | ||
*/ | ||
createFilter(): (path: string) => boolean | ||
createFilter(): (pathname: Pathname) => boolean | ||
@@ -37,3 +44,10 @@ /** | ||
*/ | ||
ignores(pathname: string): boolean | ||
ignores(pathname: Pathname): boolean | ||
/** | ||
* Returns whether pathname should be ignored or unignored | ||
* @param {string} pathname a path to check | ||
* @returns TestResult | ||
*/ | ||
test(pathname: Pathname): TestResult | ||
} | ||
@@ -50,2 +64,6 @@ | ||
declare namespace ignore { | ||
export function isPathValid (pathname: string): boolean | ||
} | ||
export default ignore |
249
index.js
// A simple implementation of make-array | ||
function make_array (subject) { | ||
function makeArray (subject) { | ||
return Array.isArray(subject) | ||
@@ -8,5 +8,13 @@ ? subject | ||
const REGEX_BLANK_LINE = /^\s+$/ | ||
const REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\!/ | ||
const REGEX_LEADING_EXCAPED_HASH = /^\\#/ | ||
const REGEX_TEST_BLANK_LINE = /^\s+$/ | ||
const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/ | ||
const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/ | ||
const REGEX_SPLITALL_CRLF = /\r?\n/g | ||
// /foo, | ||
// ./foo, | ||
// ../foo, | ||
// . | ||
// .. | ||
const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/ | ||
const SLASH = '/' | ||
@@ -161,3 +169,3 @@ const KEY_IGNORE = typeof Symbol !== 'undefined' | ||
// Check if it is not the last `'/**'` | ||
(match, index, str) => index + 6 < str.length | ||
(_, index, str) => index + 6 < str.length | ||
@@ -189,3 +197,3 @@ // case: /**/ | ||
// '*.js' doesn't match 'abc' | ||
(match, p1) => `${p1}[^\\/]*` | ||
(_, p1) => `${p1}[^\\/]*` | ||
], | ||
@@ -196,3 +204,3 @@ | ||
/(\^|\\\/)?\\\*$/, | ||
(match, p1) => { | ||
(_, p1) => { | ||
const prefix = p1 | ||
@@ -272,7 +280,7 @@ // '\^': | ||
// A simple cache, because an ignore rule only has only one certain meaning | ||
const cache = Object.create(null) | ||
const regexCache = Object.create(null) | ||
// @param {pattern} | ||
const make_regex = (pattern, negative, ignorecase) => { | ||
const r = cache[pattern] | ||
const makeRegex = (pattern, negative, ignorecase) => { | ||
const r = regexCache[pattern] | ||
if (r) { | ||
@@ -291,3 +299,3 @@ return r | ||
return cache[pattern] = ignorecase | ||
return regexCache[pattern] = ignorecase | ||
? new RegExp(source, 'i') | ||
@@ -297,6 +305,8 @@ : new RegExp(source) | ||
const isString = subject => typeof subject === 'string' | ||
// > A blank line matches no files, so it can serve as a separator for readability. | ||
const checkPattern = pattern => pattern | ||
&& typeof pattern === 'string' | ||
&& !REGEX_BLANK_LINE.test(pattern) | ||
&& isString(pattern) | ||
&& !REGEX_TEST_BLANK_LINE.test(pattern) | ||
@@ -306,2 +316,18 @@ // > A line starting with # serves as a comment. | ||
const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF) | ||
class IgnoreRule { | ||
constructor ( | ||
origin, | ||
pattern, | ||
negative, | ||
regex | ||
) { | ||
this.origin = origin | ||
this.pattern = pattern | ||
this.negative = negative | ||
this.regex = regex | ||
} | ||
} | ||
const createRule = (pattern, ignorecase) => { | ||
@@ -320,10 +346,10 @@ const origin = pattern | ||
// > begin with a literal "!", for example, `"\!important!.txt"`. | ||
.replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!') | ||
.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') | ||
// > Put a backslash ("\") in front of the first hash for patterns that | ||
// > begin with a hash. | ||
.replace(REGEX_LEADING_EXCAPED_HASH, '#') | ||
.replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') | ||
const regex = make_regex(pattern, negative, ignorecase) | ||
const regex = makeRegex(pattern, negative, ignorecase) | ||
return { | ||
return new IgnoreRule( | ||
origin, | ||
@@ -333,6 +359,37 @@ pattern, | ||
regex | ||
) | ||
} | ||
const throwError = (message, Ctor) => { | ||
throw new Ctor(message) | ||
} | ||
const returnFalse = () => false | ||
const checkPath = (path, doThrow) => { | ||
if (!isString(path)) { | ||
return doThrow( | ||
`path must be a string, but got \`${path}\``, | ||
TypeError | ||
) | ||
} | ||
// We don't know if we should ignore '', so throw | ||
if (!path) { | ||
return doThrow(`path must not be empty`, TypeError) | ||
} | ||
// | ||
if (REGEX_TEST_INVALID_PATH.test(path)) { | ||
const r = '`path.relative()`d' | ||
return doThrow( | ||
`path should be a ${r} string, but got "${path}"`, | ||
RangeError | ||
) | ||
} | ||
return true | ||
} | ||
class IgnoreBase { | ||
class Ignore { | ||
constructor ({ | ||
@@ -348,15 +405,31 @@ ignorecase = true | ||
_initCache () { | ||
this._cache = Object.create(null) | ||
this._ignoreCache = Object.create(null) | ||
this._testCache = Object.create(null) | ||
} | ||
// @param {Array.<string>|string|Ignore} pattern | ||
_addPattern (pattern) { | ||
// #32 | ||
if (pattern && pattern[KEY_IGNORE]) { | ||
this._rules = this._rules.concat(pattern._rules) | ||
this._added = true | ||
return | ||
} | ||
if (checkPattern(pattern)) { | ||
const rule = createRule(pattern, this._ignorecase) | ||
this._added = true | ||
this._rules.push(rule) | ||
} | ||
} | ||
// @param {Array<string> | string | Ignore} pattern | ||
add (pattern) { | ||
this._added = false | ||
if (typeof pattern === 'string') { | ||
pattern = pattern.split(/\r?\n/g) | ||
} | ||
makeArray( | ||
isString(pattern) | ||
? splitPattern(pattern) | ||
: pattern | ||
).forEach(this._addPattern, this) | ||
make_array(pattern).forEach(this._addPattern, this) | ||
// Some rules have just added to the ignore, | ||
@@ -376,37 +449,51 @@ // making the behavior changed. | ||
_addPattern (pattern) { | ||
// #32 | ||
if (pattern && pattern[KEY_IGNORE]) { | ||
this._rules = this._rules.concat(pattern._rules) | ||
this._added = true | ||
return | ||
} | ||
if (checkPattern(pattern)) { | ||
const rule = createRule(pattern, this._ignorecase) | ||
this._added = true | ||
this._rules.push(rule) | ||
} | ||
} | ||
// | ignored:unignored | ||
// negative | 0:0 | 0:1 | 1:0 | 1:1 | ||
// -------- | ------- | ------- | ------- | -------- | ||
// 0 | TEST | TEST | SKIP | X | ||
// 1 | TESTIF | SKIP | TEST | X | ||
filter (paths) { | ||
return make_array(paths).filter(path => this._filter(path)) | ||
} | ||
// - SKIP: always skip | ||
// - TEST: always test | ||
// - TESTIF: only test if checkUnignored | ||
createFilter () { | ||
return path => this._filter(path) | ||
} | ||
// @param {boolean} whether should check if the path is unignored, | ||
// setting `checkUnignored` to `false` could reduce additional | ||
// path matching. | ||
ignores (path) { | ||
return !this._filter(path) | ||
} | ||
// @returns {TestResult} true if a file is ignored | ||
_testOne (path, checkUnignored) { | ||
let ignored = false | ||
let unignored = false | ||
// @returns `Boolean` true if the `path` is NOT ignored | ||
_filter (path, slices) { | ||
if (!path) { | ||
return false | ||
this._rules.forEach(rule => { | ||
const {negative} = rule | ||
if ( | ||
unignored === negative && ignored !== unignored | ||
|| negative && !ignored && !unignored && !checkUnignored | ||
) { | ||
return | ||
} | ||
const matched = rule.regex.test(path) | ||
if (matched) { | ||
ignored = !negative | ||
unignored = negative | ||
} | ||
}) | ||
return { | ||
ignored, | ||
unignored | ||
} | ||
} | ||
if (path in this._cache) { | ||
return this._cache[path] | ||
// @returns {TestResult} | ||
_test (path, cache, checkUnignored, slices) { | ||
checkPath(path, throwError) | ||
if (path in cache) { | ||
return cache[path] | ||
} | ||
@@ -422,27 +509,37 @@ | ||
return this._cache[path] = slices.length | ||
// If the path has no parent directory, just test it | ||
if (!slices.length) { | ||
return cache[path] = this._testOne(path, checkUnignored) | ||
} | ||
const parent = this._test( | ||
slices.join(SLASH) + SLASH, | ||
cache, | ||
checkUnignored, | ||
slices | ||
) | ||
// If the path contains a parent directory, check the parent first | ||
return cache[path] = parent.ignored | ||
// > 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) | ||
? parent | ||
: this._testOne(path, checkUnignored) | ||
} | ||
// Or only test the path | ||
: this._test(path) | ||
ignores (path) { | ||
return this._test(path, this._ignoreCache, false).ignored | ||
} | ||
// @returns {Boolean} true if a file is NOT ignored | ||
_test (path) { | ||
// Explicitly define variable type by setting matched to `0` | ||
let matched = 0 | ||
createFilter () { | ||
return path => !this.ignores(path) | ||
} | ||
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) | ||
} | ||
}) | ||
filter (paths) { | ||
return makeArray(paths).filter(this.createFilter()) | ||
} | ||
return !matched | ||
// Returns {ignored: boolean, unignored: boolean} | ||
test (path) { | ||
return this._test(path, this._testCache, true) | ||
} | ||
@@ -462,3 +559,3 @@ } | ||
) { | ||
const filter = IgnoreBase.prototype._filter | ||
const filter = Ignore.prototype._filter | ||
@@ -471,3 +568,3 @@ /* eslint no-control-regex: "off" */ | ||
IgnoreBase.prototype._filter = function filterWin32 (path, slices) { | ||
Ignore.prototype._filter = function filterWin32 (path, slices) { | ||
path = make_posix(path) | ||
@@ -478,2 +575,6 @@ return filter.call(this, path, slices) | ||
module.exports = options => new IgnoreBase(options) | ||
const factory = options => new Ignore(options) | ||
factory.isPathValid = path => checkPath(path, returnFalse) | ||
module.exports = factory |
274
legacy.js
@@ -8,9 +8,17 @@ 'use strict'; | ||
// A simple implementation of make-array | ||
function make_array(subject) { | ||
function makeArray(subject) { | ||
return Array.isArray(subject) ? subject : [subject]; | ||
} | ||
var REGEX_BLANK_LINE = /^\s+$/; | ||
var REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\!/; | ||
var REGEX_LEADING_EXCAPED_HASH = /^\\#/; | ||
var REGEX_TEST_BLANK_LINE = /^\s+$/; | ||
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/; | ||
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/; | ||
var REGEX_SPLITALL_CRLF = /\r?\n/g; | ||
// /foo, | ||
// ./foo, | ||
// ../foo, | ||
// . | ||
// .. | ||
var REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/; | ||
var SLASH = '/'; | ||
@@ -150,3 +158,3 @@ var KEY_IGNORE = typeof Symbol !== 'undefined' ? Symbol.for('node-ignore') | ||
// Check if it is not the last `'/**'` | ||
function (match, index, str) { | ||
function (_, index, str) { | ||
return index + 6 < str.length | ||
@@ -179,3 +187,3 @@ | ||
// '*.js' doesn't match 'abc' | ||
function (match, p1) { | ||
function (_, p1) { | ||
return `${p1}[^\\/]*`; | ||
@@ -185,3 +193,3 @@ }], | ||
// trailing wildcard | ||
[/(\^|\\\/)?\\\*$/, function (match, p1) { | ||
[/(\^|\\\/)?\\\*$/, function (_, p1) { | ||
var prefix = p1 | ||
@@ -249,7 +257,7 @@ // '\^': | ||
// A simple cache, because an ignore rule only has only one certain meaning | ||
var cache = Object.create(null); | ||
var regexCache = Object.create(null); | ||
// @param {pattern} | ||
var make_regex = function make_regex(pattern, negative, ignorecase) { | ||
var r = cache[pattern]; | ||
var makeRegex = function makeRegex(pattern, negative, ignorecase) { | ||
var r = regexCache[pattern]; | ||
if (r) { | ||
@@ -265,8 +273,12 @@ return r; | ||
return cache[pattern] = ignorecase ? new RegExp(source, 'i') : new RegExp(source); | ||
return regexCache[pattern] = ignorecase ? new RegExp(source, 'i') : new RegExp(source); | ||
}; | ||
var isString = function isString(subject) { | ||
return typeof subject === 'string'; | ||
}; | ||
// > A blank line matches no files, so it can serve as a separator for readability. | ||
var checkPattern = function checkPattern(pattern) { | ||
return pattern && typeof pattern === 'string' && !REGEX_BLANK_LINE.test(pattern) | ||
return pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) | ||
@@ -277,2 +289,15 @@ // > A line starting with # serves as a comment. | ||
var splitPattern = function splitPattern(pattern) { | ||
return pattern.split(REGEX_SPLITALL_CRLF); | ||
}; | ||
var IgnoreRule = function IgnoreRule(origin, pattern, negative, regex) { | ||
_classCallCheck(this, IgnoreRule); | ||
this.origin = origin; | ||
this.pattern = pattern; | ||
this.negative = negative; | ||
this.regex = regex; | ||
}; | ||
var createRule = function createRule(pattern, ignorecase) { | ||
@@ -291,19 +316,41 @@ var origin = pattern; | ||
// > begin with a literal "!", for example, `"\!important!.txt"`. | ||
.replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!') | ||
.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') | ||
// > Put a backslash ("\") in front of the first hash for patterns that | ||
// > begin with a hash. | ||
.replace(REGEX_LEADING_EXCAPED_HASH, '#'); | ||
.replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#'); | ||
var regex = make_regex(pattern, negative, ignorecase); | ||
var regex = makeRegex(pattern, negative, ignorecase); | ||
return { | ||
origin, | ||
pattern, | ||
negative, | ||
regex | ||
}; | ||
return new IgnoreRule(origin, pattern, negative, regex); | ||
}; | ||
var IgnoreBase = function () { | ||
function IgnoreBase() { | ||
var throwError = function throwError(message, Ctor) { | ||
throw new Ctor(message); | ||
}; | ||
var returnFalse = function returnFalse() { | ||
return false; | ||
}; | ||
var checkPath = function checkPath(path, doThrow) { | ||
if (!isString(path)) { | ||
return doThrow(`path must be a string, but got \`${path}\``, TypeError); | ||
} | ||
// We don't know if we should ignore '', so throw | ||
if (!path) { | ||
return doThrow(`path must not be empty`, TypeError); | ||
} | ||
// | ||
if (REGEX_TEST_INVALID_PATH.test(path)) { | ||
var r = '`path.relative()`d'; | ||
return doThrow(`path should be a ${r} string, but got "${path}"`, RangeError); | ||
} | ||
return true; | ||
}; | ||
var Ignore = function () { | ||
function Ignore() { | ||
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, | ||
@@ -313,3 +360,3 @@ _ref$ignorecase = _ref.ignorecase, | ||
_classCallCheck(this, IgnoreBase); | ||
_classCallCheck(this, Ignore); | ||
@@ -322,10 +369,27 @@ this._rules = []; | ||
_createClass(IgnoreBase, [{ | ||
_createClass(Ignore, [{ | ||
key: '_initCache', | ||
value: function _initCache() { | ||
this._cache = Object.create(null); | ||
this._ignoreCache = Object.create(null); | ||
this._testCache = Object.create(null); | ||
} | ||
}, { | ||
key: '_addPattern', | ||
value: function _addPattern(pattern) { | ||
// #32 | ||
if (pattern && pattern[KEY_IGNORE]) { | ||
this._rules = this._rules.concat(pattern._rules); | ||
this._added = true; | ||
return; | ||
} | ||
// @param {Array.<string>|string|Ignore} pattern | ||
if (checkPattern(pattern)) { | ||
var rule = createRule(pattern, this._ignorecase); | ||
this._added = true; | ||
this._rules.push(rule); | ||
} | ||
} | ||
// @param {Array<string> | string | Ignore} pattern | ||
}, { | ||
@@ -336,8 +400,4 @@ key: 'add', | ||
if (typeof pattern === 'string') { | ||
pattern = pattern.split(/\r?\n/g); | ||
} | ||
makeArray(isString(pattern) ? splitPattern(pattern) : pattern).forEach(this._addPattern, this); | ||
make_array(pattern).forEach(this._addPattern, this); | ||
// Some rules have just added to the ignore, | ||
@@ -359,53 +419,55 @@ // making the behavior changed. | ||
} | ||
}, { | ||
key: '_addPattern', | ||
value: function _addPattern(pattern) { | ||
// #32 | ||
if (pattern && pattern[KEY_IGNORE]) { | ||
this._rules = this._rules.concat(pattern._rules); | ||
this._added = true; | ||
return; | ||
} | ||
if (checkPattern(pattern)) { | ||
var rule = createRule(pattern, this._ignorecase); | ||
this._added = true; | ||
this._rules.push(rule); | ||
} | ||
} | ||
// | ignored:unignored | ||
// negative | 0:0 | 0:1 | 1:0 | 1:1 | ||
// -------- | ------- | ------- | ------- | -------- | ||
// 0 | TEST | TEST | SKIP | X | ||
// 1 | TESTIF | SKIP | TEST | X | ||
// - SKIP: always skip | ||
// - TEST: always test | ||
// - TESTIF: only test if checkUnignored | ||
// @param {boolean} whether should check if the path is unignored, | ||
// setting `checkUnignored` to `false` could reduce additional | ||
// path matching. | ||
// @returns {TestResult} true if a file is ignored | ||
}, { | ||
key: 'filter', | ||
value: function filter(paths) { | ||
var _this = this; | ||
key: '_testOne', | ||
value: function _testOne(path, checkUnignored) { | ||
var ignored = false; | ||
var unignored = false; | ||
return make_array(paths).filter(function (path) { | ||
return _this._filter(path); | ||
this._rules.forEach(function (rule) { | ||
var negative = rule.negative; | ||
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) { | ||
return; | ||
} | ||
var matched = rule.regex.test(path); | ||
if (matched) { | ||
ignored = !negative; | ||
unignored = negative; | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'createFilter', | ||
value: function createFilter() { | ||
var _this2 = this; | ||
return function (path) { | ||
return _this2._filter(path); | ||
return { | ||
ignored, | ||
unignored | ||
}; | ||
} | ||
}, { | ||
key: 'ignores', | ||
value: function ignores(path) { | ||
return !this._filter(path); | ||
} | ||
// @returns `Boolean` true if the `path` is NOT ignored | ||
// @returns {TestResult} | ||
}, { | ||
key: '_filter', | ||
value: function _filter(path, slices) { | ||
if (!path) { | ||
return false; | ||
} | ||
key: '_test', | ||
value: function _test(path, cache, checkUnignored, slices) { | ||
checkPath(path, throwError); | ||
if (path in this._cache) { | ||
return this._cache[path]; | ||
if (path in cache) { | ||
return cache[path]; | ||
} | ||
@@ -421,33 +483,45 @@ | ||
return this._cache[path] = slices.length | ||
// If the path has no parent directory, just test it | ||
if (!slices.length) { | ||
return cache[path] = this._testOne(path, checkUnignored); | ||
} | ||
var parent = this._test(slices.join(SLASH) + SLASH, cache, checkUnignored, slices); | ||
// If the path contains a parent directory, check the parent first | ||
return cache[path] = parent.ignored | ||
// > 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) | ||
? parent : this._testOne(path, checkUnignored); | ||
} | ||
}, { | ||
key: 'ignores', | ||
value: function ignores(path) { | ||
return this._test(path, this._ignoreCache, false).ignored; | ||
} | ||
}, { | ||
key: 'createFilter', | ||
value: function createFilter() { | ||
var _this = this; | ||
// Or only test the path | ||
: this._test(path); | ||
return function (path) { | ||
return !_this.ignores(path); | ||
}; | ||
} | ||
}, { | ||
key: 'filter', | ||
value: function filter(paths) { | ||
return makeArray(paths).filter(this.createFilter()); | ||
} | ||
// @returns {Boolean} true if a file is NOT ignored | ||
// Returns {ignored: boolean, unignored: boolean} | ||
}, { | ||
key: '_test', | ||
value: function _test(path) { | ||
// Explicitly define variable type by setting matched to `0` | ||
var matched = 0; | ||
this._rules.forEach(function (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); | ||
} | ||
}); | ||
return !matched; | ||
key: 'test', | ||
value: function test(path) { | ||
return this._test(path, this._testCache, true); | ||
} | ||
}]); | ||
return IgnoreBase; | ||
return Ignore; | ||
}(); | ||
@@ -463,3 +537,3 @@ | ||
typeof process !== 'undefined' && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === 'win32')) { | ||
var filter = IgnoreBase.prototype._filter; | ||
var filter = Ignore.prototype._filter; | ||
@@ -472,3 +546,3 @@ /* eslint no-control-regex: "off" */ | ||
IgnoreBase.prototype._filter = function filterWin32(path, slices) { | ||
Ignore.prototype._filter = function filterWin32(path, slices) { | ||
path = make_posix(path); | ||
@@ -479,4 +553,10 @@ return filter.call(this, path, slices); | ||
module.exports = function (options) { | ||
return new IgnoreBase(options); | ||
var factory = function factory(options) { | ||
return new Ignore(options); | ||
}; | ||
factory.isPathValid = function (path) { | ||
return checkPath(path, returnFalse); | ||
}; | ||
module.exports = factory; |
{ | ||
"name": "ignore", | ||
"version": "4.0.6", | ||
"version": "5.0.0", | ||
"description": "Ignore is a manager and filter for .gitignore rules.", | ||
@@ -17,5 +17,7 @@ "files": [ | ||
"test:git": "tap test/git-check-ignore.js", | ||
"test:ignore": "tap test/ignore.js --coverage", | ||
"test-no-cov": "npm run test:lint && npm run test:tsc && tap test/*.js --coverage", | ||
"test": "npm run test-no-cov", | ||
"test:ignore": "tap test/ignore.js", | ||
"test:others": "tap test/others.js", | ||
"test:cases": "tap test/*.js --coverage", | ||
"test-no-report": "npm run test:lint && npm run test:tsc && npm run test:cases", | ||
"test": "npm run test-no-report", | ||
"posttest": "tap --coverage-report=html && codecov" | ||
@@ -22,0 +24,0 @@ }, |
115
README.md
@@ -55,6 +55,5 @@ <table><thead> | ||
- [`Pathname` Conventions](#pathname-conventions) | ||
- [Guide for 2.x -> 3.x](#upgrade-2x---3x) | ||
- [Guide for 3.x -> 4.x](#upgrade-3x---4x) | ||
- See Also: | ||
- [`glob-gitignore`](https://www.npmjs.com/package/glob-gitignore) matches files using patterns and filters them according to gitignore rules. | ||
- [Upgrade Guide](#upgrade-guide) | ||
@@ -156,3 +155,3 @@ ## Usage | ||
## .filter(paths: Array<Pathname>): Array<Pathname> | ||
## .filter(paths: Array<Pathname>): Array<Pathname> | ||
@@ -171,9 +170,9 @@ ```ts | ||
`Pathname` should be a string that have been `path.join()`ed, or the return value of `path.relative()` to the current directory. | ||
`Pathname` should be a string that have been `path.join()`ed, or the return value of `path.relative()` to the current directory, | ||
```js | ||
// WRONG | ||
// WRONG, an error will be thrown | ||
ig.ignores('./abc') | ||
// WRONG, for it will never happen. | ||
// WRONG, for it will never happen, and an error will be thrown | ||
// If the gitignore rule locates at the root directory, | ||
@@ -218,2 +217,22 @@ // `'/abc'` should be changed to `'abc'`. | ||
#### 2. filenames and dirnames | ||
`node-ignore` does NO `fs.stat` during path matching, so for the example below: | ||
```js | ||
// First, we add a ignore pattern to ignore a directory | ||
ig.add('config/') | ||
// `ig` does NOT know if 'config', in the real world, | ||
// is a normal file, directory or something. | ||
ig.ignores('config') | ||
// `ig` treats `config` as a file, so it returns `false` | ||
ig.ignores('config/') | ||
// returns `true` | ||
``` | ||
Specially for people who develop some library based on `node-ignore`, it is important to understand that. | ||
Usually, you could use [`glob`](http://npmjs.org/package/glob) with `option.mark = true` to fetch the structure of the current directory: | ||
@@ -237,17 +256,2 @@ | ||
#### 2. filenames and dirnames | ||
`node-ignore` does NO `fs.stat` during path matching, so for the example below: | ||
```js | ||
ig.add('config/') | ||
// `ig` does NOT know if 'config' is a normal file, directory or something | ||
ig.ignores('config') // And it returns `false` | ||
ig.ignores('config/') // returns `true` | ||
``` | ||
Specially for people who develop some library based on `node-ignore`, it is important to understand that. | ||
## .ignores(pathname: Pathname): boolean | ||
@@ -269,5 +273,17 @@ | ||
## .test(pathname: Pathname) since 5.0.0 | ||
Returns `TestResult` | ||
```ts | ||
interface TestResult { | ||
ignored: boolean | ||
// true if the `pathname` is finally unignored by some negative pattern | ||
unignored: boolean | ||
} | ||
``` | ||
## `options.ignorecase` since 4.0.0 | ||
Similar as the `core.ignorecase` option of [git-config](https://git-scm.com/docs/git-config), `node-ignore` will be case insensitive if `options.ignorecase` is set to `true` (default value), otherwise case sensitive. | ||
Similar as the `core.ignorecase` option of [git-config](https://git-scm.com/docs/git-config), `node-ignore` will be case insensitive if `options.ignorecase` is set to `true` (the default value), otherwise case sensitive. | ||
@@ -284,2 +300,10 @@ ```js | ||
## static `ignore.isPathValid(pathname): boolean` since 5.0.0 | ||
Check whether the `pathname` is valid according to the [convention](#1-pathname-should-be-a-pathrelatived-pathname). | ||
```js | ||
ignore.isPathValid('./foo') // false | ||
``` | ||
**** | ||
@@ -289,8 +313,41 @@ | ||
## Upgrade 2.x -> 3.x | ||
## Upgrade 4.x -> 5.x | ||
- All `options` of 2.x are unnecessary and removed, so just remove them. | ||
- `ignore()` instance is no longer an [`EventEmitter`](nodejs.org/api/events.html), and all events are unnecessary and removed. | ||
- `.addIgnoreFile()` is removed, see the [.addIgnoreFile](#addignorefilepath) section for details. | ||
Since `5.0.0`, if an invalid `Pathname` passed into `ig.ignores()`, an error will be thrown, while `ignore < 5.0.0` did not make sure what the return value was, as well as | ||
```ts | ||
.ignores(pathname: Pathname): boolean | ||
.filter(pathnames: Array<Pathname>): Array<Pathname> | ||
.createFilter(): (pathname: Pathname) => boolean | ||
.test(pathname: Pathname): {ignored: boolean, unignored: boolean} | ||
``` | ||
See the convention [here](#1-pathname-should-be-a-pathrelatived-pathname) for details. | ||
If there are invalid pathnames, the conversion and filtration should be done by users. | ||
```js | ||
const path = require('path') | ||
const {isPathValid} = require('ignore') // introduced in 5.0.0 | ||
const paths = [ | ||
// invalid | ||
////////////////// | ||
'', | ||
false, | ||
'../foo', | ||
'.', | ||
////////////////// | ||
// valid | ||
'foo' | ||
] | ||
.filter(isValidPath) | ||
ig.filter(paths) | ||
``` | ||
## Upgrade 3.x -> 4.x | ||
@@ -304,2 +361,8 @@ | ||
## Upgrade 2.x -> 3.x | ||
- All `options` of 2.x are unnecessary and removed, so just remove them. | ||
- `ignore()` instance is no longer an [`EventEmitter`](nodejs.org/api/events.html), and all events are unnecessary and removed. | ||
- `.addIgnoreFile()` is removed, see the [.addIgnoreFile](#addignorefilepath) section for details. | ||
**** | ||
@@ -306,0 +369,0 @@ |
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
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
44442
962
371
1