pax_global_header00006660000000000000000000000064141575256440014527gustar00rootroot0000000000000052 comment=5768b70cf856289c9a6488e3ba2053df78b7c2c7 node-ignore-5.2.0/000077500000000000000000000000001415752564400137415ustar00rootroot00000000000000node-ignore-5.2.0/.babelrc000066400000000000000000000001111415752564400153250ustar00rootroot00000000000000{ "presets": [ // Defaults to es2015 "@babel/preset-env" ] } node-ignore-5.2.0/.eslintignore000066400000000000000000000000711415752564400164420ustar00rootroot00000000000000/build /coverage /no-track /test/ts/simple.js /legacy.js node-ignore-5.2.0/.eslintrc.js000066400000000000000000000007571415752564400162110ustar00rootroot00000000000000// http://eslint.org/docs/user-guide/configuring module.exports = { // Uses `require.resolve` to support npm linked eslint-config extends: require.resolve('eslint-config-ostai'), root: true, rules: { 'no-underscore-dangle': ['error', { allowAfterThis: true, enforceInMethodNames: false, // node-ignore only allow: ['_rules', '_test'] }], indent: ['error', 2, { MemberExpression: 0, // Eslint bug ignoreComments: true }] } } node-ignore-5.2.0/.github/000077500000000000000000000000001415752564400153015ustar00rootroot00000000000000node-ignore-5.2.0/.github/FUNDING.yml000066400000000000000000000012051415752564400171140ustar00rootroot00000000000000# These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # kaelzhang open_collective: node-ignore ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] node-ignore-5.2.0/.github/workflows/000077500000000000000000000000001415752564400173365ustar00rootroot00000000000000node-ignore-5.2.0/.github/workflows/nodejs.yml000066400000000000000000000007441415752564400213500ustar00rootroot00000000000000name: Node CI on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [12.x, 14.x, 16.x] steps: - uses: actions/checkout@v1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: npm install, build, and test run: | npm install npm run build --if-present npm test env: CI: true node-ignore-5.2.0/.gitignore000066400000000000000000000010221415752564400157240ustar00rootroot00000000000000# test test/ts/*.js # coverage coverage .nyc_output /legacy.js # yarn *.lock # php files index.php # Numerous always-ignore extensions *.bak *.patch *.diff *.err *.orig *.log *.rej *.swo *.swp *.zip *.vi *~ *.sass-cache package-lock.json # npm package *.tgz # OS or Editor folders .DS_Store ._* .cache .project .settings .tmproj *.esproj *.sublime-project nbproject thumbs.db *.*-workspace # Folders to ignore .hg .svn .CVS .idea node_modules old/ *-old/ *-notrack/ no-track/ build/ combo/ reference/ jscoverage_lib/ temp/ node-ignore-5.2.0/.travis.yml000066400000000000000000000002001415752564400160420ustar00rootroot00000000000000arch: - amd64 - ppc64le language: node_js sudo: false node_js: - "10" - "12" - "14" before_install: - git --version node-ignore-5.2.0/CHANGELOG.md000066400000000000000000000023641415752564400155570ustar00rootroot00000000000000# `node-ignore` 5 ChangeLog # 5.x ## 2018-08-14, Version 5.0.1 - **PATCH**: fixes for windows. - **PATCH**: improves tests for typescript and windows. ## 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 - **SEMVER-MAJOR**: Drop support for node < 6 by default. - **FEATURE**: supports the missing character ranges and sets, such as `*.[a-z]` and `*.[jJ][pP][gG]` - **FEATURE**: new option: `ignorecase` to make `ignore` case insensitive. - **FEATURE**: supports question mark which matches a single character. - **PATCH**: fixes typescript declaration. node-ignore-5.2.0/LICENSE-MIT000066400000000000000000000021071415752564400153750ustar00rootroot00000000000000Copyright (c) 2013 Kael Zhang , contributors http://kael.me/ 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.node-ignore-5.2.0/README.md000066400000000000000000000267511415752564400152330ustar00rootroot00000000000000
Linux OS X Windows Coverage Downloads
Build Status Windows Build Status Coverage Status npm module downloads per month
# ignore `ignore` is a manager, filter and parser which implemented in pure JavaScript according to the [.gitignore spec 2.22.1](http://git-scm.com/docs/gitignore). `ignore` is used by eslint, gitbook and [many others](https://www.npmjs.com/browse/depended/ignore). Pay **ATTENTION** that [`minimatch`](https://www.npmjs.org/package/minimatch) (which used by `fstream-ignore`) does not follow the gitignore spec. To filter filenames according to a .gitignore file, I recommend this npm package, `ignore`. To parse an `.npmignore` file, you should use `minimatch`, because an `.npmignore` file is parsed by npm using `minimatch` and it does not work in the .gitignore way. ### Tested on `ignore` is fully tested, and has more than **five hundreds** of unit tests. - Linux + Node: `0.8` - `7.x` - Windows + Node: `0.10` - `7.x`, node < `0.10` is not tested due to the lack of support of appveyor. Actually, `ignore` does not rely on any versions of node specially. Since `4.0.0`, ignore will no longer support `node < 6` by default, to use in node < 6, `require('ignore/legacy')`. For details, see [CHANGELOG](https://github.com/kaelzhang/node-ignore/blob/master/CHANGELOG.md). ## Table Of Main Contents - [Usage](#usage) - [`Pathname` Conventions](#pathname-conventions) - 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) ## Install ```sh npm i ignore ``` ## Usage ```js import ignore from 'ignore' const ig = ignore().add(['.abc/*', '!.abc/d/']) ``` ### Filter the given paths ```js const paths = [ '.abc/a.js', // filtered out '.abc/d/e.js' // included ] ig.filter(paths) // ['.abc/d/e.js'] ig.ignores('.abc/a.js') // true ``` ### As the filter function ```js paths.filter(ig.createFilter()); // ['.abc/d/e.js'] ``` ### Win32 paths will be handled ```js ig.filter(['.abc\\a.js', '.abc\\d\\e.js']) // if the code above runs on windows, the result will be // ['.abc\\d\\e.js'] ``` ## Why another ignore? - `ignore` is a standalone module, and is much simpler so that it could easy work with other programs, unlike [isaacs](https://npmjs.org/~isaacs)'s [fstream-ignore](https://npmjs.org/package/fstream-ignore) which must work with the modules of the fstream family. - `ignore` only contains utility methods to filter paths according to the specified ignore rules, so - `ignore` never try to find out ignore rules by traversing directories or fetching from git configurations. - `ignore` don't cares about sub-modules of git projects. - Exactly according to [gitignore man page](http://git-scm.com/docs/gitignore), fixes some known matching issues of fstream-ignore, such as: - '`/*.js`' should only match '`a.js`', but not '`abc/a.js`'. - '`**/foo`' should match '`foo`' anywhere. - Prevent re-including a file if a parent directory of that file is excluded. - Handle trailing whitespaces: - `'a '`(one space) should not match `'a '`(two spaces). - `'a \ '` matches `'a '` - All test cases are verified with the result of `git check-ignore`. # Methods ## .add(pattern: string | Ignore): this ## .add(patterns: Array): this - **pattern** `String | Ignore` An ignore pattern string, or the `Ignore` instance - **patterns** `Array` Array of ignore patterns. Adds a rule or several rules to the current manager. Returns `this` 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().add('#abc').ignores('#abc') // false ignore().add('\#abc').ignores('#abc') // true ``` `pattern` could either be a line of ignore pattern or a string of multiple ignore patterns, which means we could just `ignore().add()` the content of a ignore file: ```js ignore() .add(fs.readFileSync(filenameOfGitignore).toString()) .filter(filenames) ``` `pattern` could also be an `ignore` instance, so that we could easily inherit the rules of another `Ignore` instance. ## .addIgnoreFile(path) REMOVED in `3.x` for now. To upgrade `ignore@2.x` up to `3.x`, use ```js import fs from 'fs' if (fs.existsSync(filename)) { ignore().add(fs.readFileSync(filename).toString()) } ``` instead. ## .filter(paths: Array<Pathname>): Array<Pathname> ```ts type Pathname = string ``` Filters the given array of pathnames, and returns the filtered array. - **paths** `Array.` The array of `pathname`s to be filtered. ### `Pathname` Conventions: #### 1. `Pathname` should be a `path.relative()`d pathname `Pathname` should be a string that have been `path.join()`ed, or the return value of `path.relative()` to the current directory, ```js // WRONG, an error will be thrown ig.ignores('./abc') // WRONG, for it will never happen, and an error will be thrown // If the gitignore rule locates at the root directory, // `'/abc'` should be changed to `'abc'`. // ``` // path.relative('/', '/abc') -> 'abc' // ``` ig.ignores('/abc') // WRONG, that it is an absolute path on Windows, an error will be thrown ig.ignores('C:\\abc') // Right ig.ignores('abc') // Right ig.ignores(path.join('./abc')) // path.join('./abc') -> 'abc' ``` In other words, each `Pathname` here should be a relative path to the directory of the gitignore rules. Suppose the dir structure is: ``` /path/to/your/repo |-- a | |-- a.js | |-- .b | |-- .c |-- .DS_store ``` Then the `paths` might be like this: ```js [ 'a/a.js' '.b', '.c/.DS_store' ] ``` #### 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: ```js import glob from 'glob' glob('**', { // Adds a / character to directory matches. mark: true }, (err, files) => { if (err) { return console.error(err) } let filtered = ignore().add(patterns).filter(files) console.log(filtered) }) ``` ## .ignores(pathname: Pathname): boolean > new in 3.2.0 Returns `Boolean` whether `pathname` should be ignored. ```js ig.ignores('.abc/a.js') // true ``` ## .createFilter() Creates a filter function which could filter an array of paths with `Array.prototype.filter`. Returns `function(path)` the filter function. ## .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 } ``` - `{ignored: true, unignored: false}`: the `pathname` is ignored - `{ignored: false, unignored: true}`: the `pathname` is unignored - `{ignored: false, unignored: false}`: the `pathname` is never matched by any ignore rules. ## static `ignore.isPathValid(pathname): boolean` since 5.0.0 Check whether the `pathname` is an valid `path.relative()`d path according to the [convention](#1-pathname-should-be-a-pathrelatived-pathname). This method is **NOT** used to check if an ignore pattern is valid. ```js ignore.isPathValid('./foo') // false ``` ## ignore(options) ### `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` (the default value), otherwise case sensitive. ```js const ig = ignore({ ignorecase: false }) ig.add('*.png') ig.ignores('*.PNG') // false ``` ### `options.ignoreCase?: boolean` since 5.2.0 Which is alternative to `options.ignoreCase` ### `options.allowRelativePaths?: boolean` since 5.2.0 This option brings backward compatibility with projects which based on `ignore@4.x`. If `options.allowRelativePaths` is `true`, `ignore` will not check whether the given path to be tested is [`path.relative()`d](#pathname-conventions). However, passing a relative path, such as `'./foo'` or `'../foo'`, to test if it is ignored or not is not a good practise, which might lead to unexpected behavior ```js ignore({ allowRelativePaths: true }).ignores('../foo/bar.js') // And it will not throw ``` **** # Upgrade Guide ## Upgrade 4.x -> 5.x Since `5.0.0`, if an invalid `Pathname` passed into `ig.ignores()`, an error will be thrown, unless `options.allowRelative = true` is passed to the `Ignore` factory. 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): Array .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 import {isPathValid} from 'ignore' // introduced in 5.0.0 const paths = [ // invalid ////////////////// '', false, '../foo', '.', ////////////////// // valid 'foo' ] .filter(isValidPath) ig.filter(paths) ``` ## Upgrade 3.x -> 4.x Since `4.0.0`, `ignore` will no longer support node < 6, to use `ignore` in node < 6: ```js var ignore = require('ignore/legacy') ``` ## 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. **** # Collaborators - [@whitecolor](https://github.com/whitecolor) *Alex* - [@SamyPesse](https://github.com/SamyPesse) *Samy Pessé* - [@azproduction](https://github.com/azproduction) *Mikhail Davydov* - [@TrySound](https://github.com/TrySound) *Bogdan Chadkin* - [@JanMattner](https://github.com/JanMattner) *Jan Mattner* - [@ntwb](https://github.com/ntwb) *Stephen Edgar* - [@kasperisager](https://github.com/kasperisager) *Kasper Isager* - [@sandersn](https://github.com/sandersn) *Nathan Shively-Sanders* node-ignore-5.2.0/appveyor.yml000066400000000000000000000007271415752564400163370ustar00rootroot00000000000000# Test against this version of Node.js environment: matrix: - nodejs_version: "12" # Install scripts. (runs after repo cloning) install: # Get the latest stable version of Node.js or io.js - ps: Install-Product node $env:nodejs_version # install modules - npm install # Post-install test scripts. test_script: # Output useful info for debugging. - node --version - npm --version # run tests - npm run test:only # Don't actually build. build: off node-ignore-5.2.0/index.d.ts000066400000000000000000000027241415752564400156470ustar00rootroot00000000000000type Pathname = string interface TestResult { ignored: boolean unignored: boolean } export interface Ignore { /** * Adds one or several rules to the current manager. * @param {string[]} patterns * @returns IgnoreBase */ add(patterns: string | Ignore | readonly (string | Ignore)[]): this /** * Filters the given array of pathnames, and returns the filtered array. * NOTICE that each path here should be a relative path to the root of your repository. * @param paths the array of paths to be filtered. * @returns The filtered array of paths */ filter(pathnames: readonly Pathname[]): Pathname[] /** * Creates a filter function which could filter * an array of paths with Array.prototype.filter. */ createFilter(): (pathname: Pathname) => boolean /** * Returns Boolean whether pathname should be ignored. * @param {string} pathname a path to check * @returns 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 } interface Options { ignorecase?: boolean // For compatibility ignoreCase?: boolean allowRelativePaths?: boolean } /** * Creates new ignore manager. */ declare function ignore(options?: Options): Ignore declare namespace ignore { export function isPathValid (pathname: string): boolean } export default ignore node-ignore-5.2.0/index.js000066400000000000000000000361301415752564400154110ustar00rootroot00000000000000// A simple implementation of make-array function makeArray (subject) { return Array.isArray(subject) ? subject : [subject] } const EMPTY = '' const SPACE = ' ' const ESCAPE = '\\' 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 = '/' const KEY_IGNORE = typeof Symbol !== 'undefined' ? Symbol.for('node-ignore') /* istanbul ignore next */ : 'node-ignore' const define = (object, key, value) => Object.defineProperty(object, key, {value}) const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g const RETURN_FALSE = () => false // Sanitize the range of a regular expression // The cases are complicated, see test cases for details const sanitizeRange = range => range.replace( REGEX_REGEXP_RANGE, (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match // Invalid range (out of order) which is ok for gitignore rules but // fatal for JavaScript regular expression, so eliminate it. : EMPTY ) // See fixtures #59 const cleanRangeBackSlash = slashes => { const {length} = slashes return slashes.slice(0, length - length % 2) } // > 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`' // -> ignore-rules will not deal with it, because it costs extra `fs.stat` call // you could use option `mark: true` with `glob` // '`foo/`' should not continue with the '`..`' const REPLACERS = [ // > Trailing spaces are ignored unless they are quoted with backslash ("\") [ // (a\ ) -> (a ) // (a ) -> (a) // (a \ ) -> (a ) /\\?\s+$/, match => match.indexOf('\\') === 0 ? SPACE : EMPTY ], // replace (\ ) with ' ' [ /\\\s/g, () => SPACE ], // 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 {, // > These special characters are often called "metacharacters". [ /[\\$.|*+(){^]/g, match => `\\${match}` ], [ // > a question mark (?) matches a single character /(?!\\)\?/g, () => '[^/]' ], // leading slash [ // > 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 /^\//, () => '^' ], // replace special metacharacter slash after the leading slash [ /\//g, () => '\\/' ], [ // > 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". // Notice that the '*'s have been replaced as '\\*' /^\^*\\\*\\\*\\\//, // '**/foo' <-> 'foo' () => '^(?:.*\\/)?' ], // starting [ // there will be no leading '/' // (which has been replaced by section "leading slash") // If starts with '**', adding a '^' to the regular expression also works /^(?=[^^])/, function startingReplacer () { // If has a slash `/` at the beginning or middle return !/\/(?!$)/.test(this) // > Prior to 2.22.1 // > 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 // After 2.22.1 (compatible but clearer) // > If there is a separator at the beginning or middle (or both) // > of the pattern, then the pattern is relative to the directory // > level of the particular .gitignore file itself. // > Otherwise the pattern may also match at any level below // > the .gitignore level. ? '(?:^|\\/)' // > Otherwise, Git treats the pattern as a shell glob suitable for // > consumption by fnmatch(3) : '^' } ], // two globstars [ // Use lookahead assertions so that we could match more than one `'/**'` /\\\/\\\*\\\*(?=\\\/|$)/g, // Zero, one or several directories // should not use '*', or it will be replaced by the next replacer // Check if it is not the last `'/**'` (_, index, str) => index + 6 < str.length // case: /**/ // > 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. // '/**/' ? '(?:\\/[^\\/]+)*' // case: /** // > A trailing `"/**"` matches everything inside. // #21: everything inside but it should not include the current folder : '\\/.+' ], // intermediate wildcards [ // Never replace escaped '*' // ignore rule '\*' will match the path '*' // 'abc.*/' -> go // 'abc.*' -> skip this rule /(^|[^\\]+)\\\*(?=.+)/g, // '*.js' matches '.js' // '*.js' doesn't match 'abc' (_, p1) => `${p1}[^\\/]*` ], [ // unescape, revert step 3 except for back slash // For example, if a user escape a '\\*', // after step 3, the result will be '\\\\\\*' /\\\\\\(?=[$.|*+(){^])/g, () => ESCAPE ], [ // '\\\\' -> '\\' /\\\\/g, () => ESCAPE ], [ // > The range notation, e.g. [a-zA-Z], // > can be used to match one of the characters in a range. // `\` is escaped by step 3 /(\\)?\[([^\]/]*?)(\\*)($|\])/g, (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE // '\\[bar]' -> '\\\\[bar\\]' ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === ']' ? endEscape.length % 2 === 0 // A normal case, and it is a range notation // '[bar]' // '[bar\\\\]' ? `[${sanitizeRange(range)}${endEscape}]` // Invalid range notaton // '[bar\\]' -> '[bar\\\\]' : '[]' : '[]' ], // ending [ // 'js' will not match 'js.' // 'ab' will not match 'abc' /(?:[^*])$/, // WTF! // https://git-scm.com/docs/gitignore // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) // which re-fixes #24, #38 // > If there is a separator at the end of the pattern then the pattern // > will only match directories, otherwise the pattern can match both // > files and directories. // 'js*' will not match 'a.js' // 'js/' will not match 'a.js' // 'js' will match 'a.js' and 'a.js/' match => /\/$/.test(match) // foo/ will not match 'foo' ? `${match}$` // foo matches 'foo' and 'foo/' : `${match}(?=$|\\/$)` ], // trailing wildcard [ /(\^|\\\/)?\\\*$/, (_, p1) => { const prefix = p1 // '\^': // '/*' does not match EMPTY // '/*' does not match everything // '\\\/': // 'abc/*' does not match 'abc/' ? `${p1}[^/]+` // 'a*' matches 'a' // 'a*' matches 'aa' : '[^/]*' return `${prefix}(?=$|\\/$)` } ], ] // A simple cache, because an ignore rule only has only one certain meaning const regexCache = Object.create(null) // @param {pattern} const makeRegex = (pattern, ignoreCase) => { let source = regexCache[pattern] if (!source) { source = REPLACERS.reduce( (prev, current) => prev.replace(current[0], current[1].bind(pattern)), pattern ) regexCache[pattern] = source } return ignoreCase ? new RegExp(source, 'i') : 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 && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) // > A line starting with # serves as a comment. && pattern.indexOf('#') !== 0 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) => { const origin = pattern let negative = false // > An optional prefix "!" which negates the pattern; if (pattern.indexOf('!') === 0) { negative = true pattern = pattern.substr(1) } pattern = pattern // > Put a backslash ("\") in front of the first "!" for patterns that // > begin with a literal "!", for example, `"\!important!.txt"`. .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') // > Put a backslash ("\") in front of the first hash for patterns that // > begin with a hash. .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') const regex = makeRegex(pattern, ignoreCase) return new IgnoreRule( origin, pattern, negative, regex ) } const throwError = (message, Ctor) => { throw new Ctor(message) } const checkPath = (path, originalPath, doThrow) => { if (!isString(path)) { return doThrow( `path must be a string, but got \`${originalPath}\``, TypeError ) } // We don't know if we should ignore EMPTY, so throw if (!path) { return doThrow(`path must not be empty`, TypeError) } // Check if it is a relative path if (checkPath.isNotRelative(path)) { const r = '`path.relative()`d' return doThrow( `path should be a ${r} string, but got "${originalPath}"`, RangeError ) } return true } const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path) checkPath.isNotRelative = isNotRelative checkPath.convert = p => p class Ignore { constructor ({ ignorecase = true, ignoreCase = ignorecase, allowRelativePaths = false } = {}) { define(this, KEY_IGNORE, true) this._rules = [] this._ignoreCase = ignoreCase this._allowRelativePaths = allowRelativePaths this._initCache() } _initCache () { this._ignoreCache = Object.create(null) this._testCache = Object.create(null) } _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 | Ignore} pattern add (pattern) { this._added = false makeArray( isString(pattern) ? splitPattern(pattern) : pattern ).forEach(this._addPattern, this) // Some rules have just added to the ignore, // making the behavior changed. if (this._added) { this._initCache() } return this } // legacy addPattern (pattern) { return this.add(pattern) } // | 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 // - X: that never happen // @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 _testOne (path, checkUnignored) { let ignored = false let unignored = 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 } } // @returns {TestResult} _test (originalPath, cache, checkUnignored, slices) { const path = originalPath // Supports nullable path && checkPath.convert(originalPath) checkPath( path, originalPath, this._allowRelativePaths ? RETURN_FALSE : throwError ) return this._t(path, cache, checkUnignored, slices) } _t (path, cache, checkUnignored, slices) { if (path in cache) { return cache[path] } if (!slices) { // path/to/a.js // ['path', 'to', 'a.js'] slices = path.split(SLASH) } slices.pop() // If the path has no parent directory, just test it if (!slices.length) { return cache[path] = this._testOne(path, checkUnignored) } const parent = this._t( 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. ? parent : this._testOne(path, checkUnignored) } ignores (path) { return this._test(path, this._ignoreCache, false).ignored } createFilter () { return path => !this.ignores(path) } filter (paths) { return makeArray(paths).filter(this.createFilter()) } // @returns {TestResult} test (path) { return this._test(path, this._testCache, true) } } const factory = options => new Ignore(options) const isPathValid = path => checkPath(path && checkPath.convert(path), path, RETURN_FALSE) factory.isPathValid = isPathValid // Fixes typescript factory.default = factory module.exports = factory // Windows // -------------------------------------------------------------- /* istanbul ignore if */ if ( // Detect `process` so that it can run in browsers. typeof process !== 'undefined' && ( process.env && process.env.IGNORE_TEST_WIN32 || process.platform === 'win32' ) ) { /* eslint no-control-regex: "off" */ const makePosix = str => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, '/') checkPath.convert = makePosix // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/' // 'd:\\foo' const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i checkPath.isNotRelative = path => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path) } node-ignore-5.2.0/package.json000066400000000000000000000035411415752564400162320ustar00rootroot00000000000000{ "name": "ignore", "version": "5.2.0", "description": "Ignore is a manager and filter for .gitignore rules, the one used by eslint, gitbook and many others.", "files": [ "legacy.js", "index.js", "index.d.ts", "LICENSE-MIT" ], "scripts": { "prepublishOnly": "npm run build", "build": "babel -o legacy.js index.js", "test:lint": "eslint .", "test:tsc": "tsc ./test/ts/simple.ts --lib ES6", "test:ts": "node ./test/ts/simple.js", "tap": "tap --reporter classic", "test:git": "npm run tap test/git-check-ignore.js", "test:ignore": "npm run tap test/ignore.js", "test:others": "npm run tap test/others.js", "test:cases": "npm run tap test/*.js -- --coverage", "test:only": "npm run test:lint && npm run test:tsc && npm run test:ts && npm run test:cases", "test": "npm run test:only", "test:win32": "IGNORE_TEST_WIN32=1 npm run test", "posttest": "tap --coverage-report=html && codecov" }, "repository": { "type": "git", "url": "git@github.com:kaelzhang/node-ignore.git" }, "keywords": [ "ignore", ".gitignore", "gitignore", "npmignore", "rules", "manager", "filter", "regexp", "regex", "fnmatch", "glob", "asterisks", "regular-expression" ], "author": "kael", "license": "MIT", "bugs": { "url": "https://github.com/kaelzhang/node-ignore/issues" }, "devDependencies": { "@babel/cli": "^7.8.4", "@babel/core": "^7.9.6", "@babel/preset-env": "^7.9.6", "codecov": "^3.7.0", "debug": "^4.1.1", "eslint": "^7.0.0", "eslint-config-ostai": "^3.0.0", "eslint-plugin-import": "^2.20.2", "mkdirp": "^1.0.4", "pre-suf": "^1.1.1", "rimraf": "^3.0.2", "spawn-sync": "^2.0.0", "tap": "^14.10.7", "tmp": "0.2.1", "typescript": "^3.9.3" }, "engines": { "node": ">= 4" } } node-ignore-5.2.0/test/000077500000000000000000000000001415752564400147205ustar00rootroot00000000000000node-ignore-5.2.0/test/fixtures/000077500000000000000000000000001415752564400165715ustar00rootroot00000000000000node-ignore-5.2.0/test/fixtures/.aignore000077500000000000000000000000221415752564400202130ustar00rootroot00000000000000abc !abc/b #e \#f node-ignore-5.2.0/test/fixtures/.fakeignore000077500000000000000000000000331415752564400207030ustar00rootroot00000000000000# Actually, there's nothingnode-ignore-5.2.0/test/fixtures/.ignore-issue-2000077500000000000000000000012131415752564400213420ustar00rootroot00000000000000# git-ls-files --others --exclude-from=.git/info/exclude # Lines that start with '#' are comments. # For a project mostly in C, the following would be a good set of # exclude patterns (uncomment them if you want to use them): # *.[oa] # *~ /.project # The same type as `'/.project'` # /.settings /sharedTools/external/* thumbs.db /packs *.pyc # /.cache # /bigtxt .metadata/* *~ /sharedTools/jsApiLua.lua ._* .DS_Store # /DISABLED # /.pydevproject # /testbox *.swp /packs/packagesTree /packs/*.ini # .buildpath # The same type as `'/sharedTools/external/*'` # /resources/hooks/* # .idea .idea/* # /tags **.iml .sonar/* .*.sw? node-ignore-5.2.0/test/fixtures/cases.js000066400000000000000000000405401415752564400202300ustar00rootroot00000000000000/* eslint quote-props: ["off"] */ const fs = require('fs') const path = require('path') const debug = require('debug')('node-ignore') function readPatterns (file) { file = path.join(__dirname, file) return fs.readFileSync(file).toString() } const cases = [ // [ // 'Example', // [ // // ignore pattern // 'a' // ], // { // // 1 indicates 'a' should be ignored // 'a': 1 // } // ], [ '#59 and more cases about range notation', [ 'src/\\[foo\\]', // 1 -> 0 'src/\\[bar]', 'src/[e\\\\]', 's/[f\\\\\\\\]', 's/[a-z0-9]', // The following special cases are not described in gitignore manual 'src/[q', 'src/\\[u', 'src/[x\\]' ], { 'src/[foo]': 1, 'src/[bar]': 1, 'src/e': 1, 's/f': 1, 's/a': 1, 's/0': 1, 'src/[q': 0, 'src/[u': 1, 'src/[x': 0, 'src/[x]': 0, 'src/x': 0 } ], [ 'gitignore 2.22.1 example', [ 'doc/frotz/' ], { 'doc/frotz/': 1, 'a/doc/frotz/': 0 } ], [ '#56', [ '/*/', '!/foo/' ], { 'foo/bar.js': 0 } ], [ 'object prototype', [ '*', '!hasOwnProperty', '!a' ], { 'hasOwnProperty': 0, 'a/hasOwnProperty': 0, 'toString': 1, 'a/toString': 1 } ], [ 'a and a/', [ 'a', 'a2', 'b/', 'b2/' ], { 'a': 1, 'a2/': 1, 'b': 0, 'b2/': 1 } ], [ 'ending question mark', [ '*.web?' ], { 'a.webp': 1, 'a.webm': 1, // only match one characters 'a.webam': 0, 'a.png': 0 } ], [ 'intermediate question mark', [ 'a?c' ], { 'abc': 1, 'acc': 1, 'ac': 0, 'abbc': 0 } ], [ 'multiple question marks', [ 'a?b??' ], { 'acbdd': 1, 'acbddd': 0 } ], [ 'normal *.[oa]', [ '*.[oa]' ], { 'a.js': 0, 'a.a': 1, // test ending 'a.aa': 0, 'a.o': 1, 'a.0': 0 } ], [ 'multiple brackets', [ '*.[ab][cd][ef]' ], { 'a.ace': 1, 'a.bdf': 1, 'a.bce': 1, 'a.abc': 0, 'a.aceg': 0 } ], [ 'special case: []', [ '*.[]' ], { 'a.[]': 0, 'a.[]a': 0 } ], [ 'mixed with numbers, characters and symbols: *.[0a_]', [ '*.[0a_]' ], { 'a.0': 1, 'a.1': 0, 'a.a': 1, 'a.b': 0, 'a._': 1, 'a.=': 0 } ], [ 'range: [a-z]', [ '*.pn[a-z]' ], { 'a.pn1': 0, 'a.pn2': 0, 'a.png': 1, 'a.pna': 1 } ], [ 'range: [0-9]', [ '*.pn[0-9]' ], { 'a.pn1': 1, 'a.pn2': 1, 'a.png': 0, 'a.pna': 0 } ], [ 'multiple ranges: [0-9a-z]', [ '*.pn[0-9a-z]' ], { 'a.pn1': 1, 'a.pn2': 1, 'a.png': 1, 'a.pna': 1, 'a.pn-': 0 } ], [ // [0-z] represents 0-0A-Za-z 'special range: [0-z]', [ '*.[0-z]' ], { 'a.0': 1, 'a.9': 1, 'a.00': 0, 'a.a': 1, 'a.z': 1, 'a.zz': 0 } ], [ // If range is out of order, then omitted 'special case: range out of order: [a-9]', [ '*.[a-9]' ], { 'a.0': 0, 'a.-': 0, 'a.9': 0 } ], [ // Just treat it as normal character set 'special case: range-like character set', [ '*.[a-]' ], { 'a.a': 1, 'a.-': 1, 'a.b': 0 } ], [ 'special case: the combination of range and set', [ '*.[a-z01]' ], { 'a.a': 1, 'a.b': 1, 'a.z': 1, 'a.0': 1, 'a.1': 1, 'a.2': 0 } ], [ 'special case: 1 step range', [ '*.[0-0]' ], { 'a.0': 1, 'a.1': 0, 'a.-': 0 } ], [ 'special case: similar, but not a character set', [ '*.[a-' ], { 'a.': 0, 'a.[': 0, 'a.a': 0, 'a.-': 0 } ], [ 'related to #38', [ '*', '!abc*' ], { 'a': 1, 'abc': 0, 'abcd': 0 } ], [ '#38', [ '*', '!*/', '!foo/bar' ], { 'a': 1, 'b/c': 1, 'foo/bar': 0, 'foo/e': 1 } ], [ 'intermediate "\\ " should be unescaped to " "', [ 'abc\\ d', 'abc e', 'a\\ b\\ c' ], { 'abc d': 1, 'abc e': 1, 'abc/abc d': 1, 'abc/abc e': 1, 'abc/a b c': 1 } ], [ '#25', [ '.git/*', '!.git/config', '.ftpconfig' ], { '.ftpconfig': 1, '.git/config': 0, '.git/description': 1 } ], [ '#26: .gitignore man page sample', [ '# exclude everything except directory foo/bar', '/*', '!/foo', '/foo/*', '!/foo/bar' ], { 'no.js': 1, 'foo/no.js': 1, 'foo/bar/yes.js': 0, 'foo/bar/baz/yes.js': 0, 'boo/no.js': 1 } ], [ 'wildcard: special case, escaped wildcard', [ '*.html', '!a/b/\\*/index.html' ], { 'a/b/*/index.html': 0, 'a/b/index.html': 1 } ], [ 'wildcard: treated as a shell glob suitable for consumption by fnmatch(3)', [ '*.html', '!b/\\*/index.html' ], { 'a/b/*/index.html': 1, 'a/b/index.html': 1 } ], [ 'wildcard: with no escape', [ '*.html', '!a/b/*/index.html' ], { 'a/b/*/index.html': 0, 'a/b/index.html': 1 } ], [ '#24: a negative pattern without a trailing wildcard', [ '/node_modules/*', '!/node_modules', '!/node_modules/package' ], { 'node_modules/a/a.js': 1, 'node_modules/package/a.js': 0 } ], [ '#21: unignore with 1 globstar, reversed order', [ '!foo/bar.js', 'foo/*' ], { 'foo/bar.js': 1, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], [ '#21: unignore with 2 globstars, reversed order', [ '!foo/bar.js', 'foo/**' ], { 'foo/bar.js': 1, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], [ '#21: unignore with several groups of 2 globstars, reversed order', [ '!foo/bar.js', 'foo/**/**' ], { 'foo/bar.js': 1, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], [ '#21: unignore with 1 globstar', [ 'foo/*', '!foo/bar.js' ], { 'foo/bar.js': 0, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], [ '#21: unignore with 2 globstars', [ 'foo/**', '!foo/bar.js' ], { 'foo/bar.js': 0, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], [ 'related to #21: several groups of 2 globstars', [ 'foo/**/**', '!foo/bar.js' ], { 'foo/bar.js': 0, 'foo/bar2.js': 1, 'foo/bar/bar.js': 1 } ], // description patterns paths/expect only [ 'ignore dot files', [ '.*' ], { '.a': 1, '.gitignore': 1 } ], [ '#14, README example broken in `3.0.3`', [ '.abc/*', '!.abc/d/' ], { '.abc/a.js': 1, '.abc/d/e.js': 0 } ], [ '#14, README example broken in `3.0.3`, not negate parent folder', [ '.abc/*', // .abc/d will be ignored '!.abc/d/*' ], { '.abc/a.js': 1, // so '.abc/d/e.js' will be ignored '.abc/d/e.js': 1 } ], [ 'A blank line matches no files', [ '' ], { 'a': 0, 'a/b/c': 0 } ], [ 'A line starting with # serves as a comment.', ['#abc'], { '#abc': 0 } ], [ 'Put a backslash ("\\") in front of the first hash for patterns that begin with a hash.', ['\\#abc'], { '#abc': 1 } ], [ 'Trailing spaces are ignored unless they are quoted with backslash ("\\")', [ 'abc\\ ', // only one space left -> (abc ) 'bcd ', // no space left -> (bcd) 'cde \\ ' // two spaces -> (cde ) ], { // nothing to do with backslashes 'abc\\ ': 0, 'abc ': 0, 'abc ': 1, 'abc ': 0, 'bcd': 1, 'bcd ': 0, 'bcd ': 0, 'cde ': 1, 'cde ': 0, 'cde ': 0 }, false, true ], [ 'An optional prefix "!" which negates the pattern; any matching file excluded by a previous pattern will become included again', [ 'abc', '!abc' ], { // the parent folder is included again 'abc/a.js': 0, 'abc/': 0 } ], [ 'issue #10: It is not possible to re-include a file if a parent directory of that file is excluded', [ '/abc/', '!/abc/a.js' ], { 'abc/a.js': 1, 'abc/d/e.js': 1 } ], [ 'we did not know whether the rule is a dir first', [ 'abc', '!bcd/abc/a.js' ], { 'abc/a.js': 1, 'bcd/abc/a.js': 1 } ], [ 'Put a backslash ("\\") in front of the first "!" for patterns that begin with a literal "!"', [ '\\!abc', '\\!important!.txt' ], { '!abc': 1, 'abc': 0, 'b/!important!.txt': 1, '!important!.txt': 1 } ], [ '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', [ 'abc/' ], { // actually, node-ignore have no idea about fs.Stat, // you should `glob({mark: true})` 'abc': 0, 'abc/': 1, // Actually, if there is only a trailing slash, git also treats it as a shell glob pattern // 'abc/' should make 'bcd/abc/' ignored. 'bcd/abc/': 1 } ], [ 'If the pattern does not contain a slash /, Git treats it as a shell glob pattern', [ 'a.js', 'f/' ], { 'a.js': 1, 'b/a/a.js': 1, 'a/a.js': 1, 'b/a.jsa': 0, 'f/': 1, 'g/f/': 1 } ], [ 'Otherwise, Git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag', [ 'a/a.js' ], { 'a/a.js': 1, 'a/a.jsa': 0, 'b/a/a.js': 0, 'c/a/a.js': 0 } ], [ 'wildcards in the pattern will not match a / in the pathname.', [ 'Documentation/*.html' ], { 'Documentation/git.html': 1, 'Documentation/ppc/ppc.html': 0, 'tools/perf/Documentation/perf.html': 0 } ], [ 'A leading slash matches the beginning of the pathname', [ '/*.c' ], { 'cat-file.c': 1, 'mozilla-sha1/sha1.c': 0 } ], [ 'A leading "**" followed by a slash means match in all directories', [ '**/foo' ], { 'foo': 1, 'a/foo': 1, 'foo/a': 1, 'a/foo/a': 1, 'a/b/c/foo/a': 1 } ], [ '"**/foo/bar" matches file or directory "bar" anywhere that is directly under directory "foo"', [ '**/foo/bar' ], { 'foo/bar': 1, 'abc/foo/bar': 1, 'abc/foo/bar/': 1 } ], [ 'A trailing "/**" matches everything inside', [ 'abc/**' ], { 'abc/a/': 1, 'abc/b': 1, 'abc/d/e/f/g': 1, 'bcd/abc/a': 0, 'abc': 0 } ], [ 'A slash followed by two consecutive asterisks then a slash matches zero or more directories', [ 'a/**/b' ], { 'a/b': 1, 'a/x/b': 1, 'a/x/y/b': 1, 'b/a/b': 0 } ], [ 'add a file content', readPatterns('.aignore'), { 'abc/a.js': 1, 'abc/b/b.js': 1, '#e': 0, '#f': 1 } ], // old test cases [ 'should excape metacharacters of regular expressions', [ '*.js', '!\\*.js', '!a#b.js', '!?.js', // comments '#abc', '\\#abc' ], { '*.js': 0, 'abc.js': 1, 'a#b.js': 0, 'abc': 0, '#abc': 1, '?.js': 0 } ], [ 'issue #2: question mark should not break all things', readPatterns('.ignore-issue-2'), { '.project': 1, // remain 'abc/.project': 0, '.a.sw': 0, '.a.sw?': 1, 'thumbs.db': 1 } ], [ 'dir ended with "*"', [ 'abc/*' ], { 'abc': 0 } ], [ 'file ended with "*"', [ 'abc.js*' ], { 'abc.js/': 1, 'abc.js/abc': 1, 'abc.jsa/': 1, 'abc.jsa/abc': 1 } ], [ 'wildcard as filename', [ '*.b' ], { 'b/a.b': 1, 'b/.b': 1, 'b/.ba': 0, 'b/c/a.b': 1 } ], [ 'slash at the beginning and come with a wildcard', [ '/*.c' ], { '.c': 1, 'c.c': 1, 'c/c.c': 0, 'c/d': 0 } ], [ 'dot file', [ '.d' ], { '.d': 1, '.dd': 0, 'd.d': 0, 'd/.d': 1, 'd/d.d': 0, 'd/e': 0 } ], [ 'dot dir', [ '.e' ], { '.e/': 1, '.ee/': 0, 'e.e/': 0, '.e/e': 1, 'e/.e': 1, 'e/e.e': 0, 'e/f': 0 } ], [ 'node modules: once', [ 'node_modules/' ], { 'node_modules/gulp/node_modules/abc.md': 1, 'node_modules/gulp/node_modules/abc.json': 1 } ], [ 'node modules: sub directories', [ 'node_modules' ], { 'a/b/node_modules/abc.md': 1 } ], [ 'node modules: twice', [ 'node_modules/', 'node_modules/' ], { 'node_modules/gulp/node_modules/abc.md': 1, 'node_modules/gulp/node_modules/abc.json': 1 } ], [ 'unicode characters in windows paths', [ 'test' ], { 'some/path/to/test/ignored.js': 1, 'some/special/path/to/目录/test/ignored.js': 1 }, false, true // git-check-ignore fails as git converts special chars to escaped unicode before printing ] ] const IS_WINDOWS = process.platform === 'win32' if (!IS_WINDOWS && !process.env.IGNORE_TEST_WIN32) { cases.push( [ 'linux: back slashes on paths', [ 'a', 'b\\\\c' ], { 'b\\c/a.md': 1, 'a\\b/a.js': 0, 'a\\b/a': 1, 'a/a.js': 1 } ], [ '#59: test cases for linux only', [ 'src/\\[foo\\]', // 1 -> 0 'src/\\[foo2\\\\]', // 2 -> 1 'src/\\[foo3\\\\\\]', // 3 -> 1 'src/\\[foo4\\\\\\\\]', // 4 -> 2 'src/\\[foo5\\\\\\\\\\]', // 5 -> 2 'src/\\[foo6\\\\\\\\\\\\]', // 6 -> 3 'src/\\[bar]', 'src/[e\\\\]', 's/[f\\\\\\\\]', 's/[a-z0-9]' ], { 'src/[foo]': 1, 'src/[foo2\\]': 1, // Seems the followings are side-effects, // however, we will implement these 'src/[foo3\\]': 1, 'src/[foo4\\\\]': 1, 'src/[foo5\\\\]': 1, 'src/[foo6\\\\\\]': 1, 'src/[bar]': 1, 'src/e': 1, 'src/\\': 1, 's/f': 1, 's/\\': 1, 's/a': 1, 's/0': 1 } ] ) } const cases_to_test_only = cases.filter(c => c[3]) const real_cases = cases_to_test_only.length ? cases_to_test_only : cases exports.cases = iteratee => { real_cases.forEach(c => { const description = c[0] const patterns = c[1] const paths_object = c[2] const skip_test_fixture = c[4] // All paths to test const paths = Object.keys(paths_object) // paths that NOT ignored let expected = paths .filter(p => !paths_object[p]) .sort() function expect_result (t, result, mapper) { if (mapper) { expected = expected.map(mapper) } t.deepEqual(result.sort(), expected.sort()) } iteratee({ description, patterns, paths_object, skip_test_fixture, paths, expected, expect_result }) }) } // For local testing purpose const ENV_KEYS = [ 'IGNORE_ONLY_FILTER', 'IGNORE_ONLY_CREATE_FILTER', 'IGNORE_ONLY_IGNORES', 'IGNORE_ONLY_WIN32', 'IGNORE_ONLY_FIXTURES', 'IGNORE_ONLY_OTHERS' ] const envs = {} let hasOnly = false ENV_KEYS.forEach(key => { const value = !!process.env[key] envs[key] = value if (value) { hasOnly = true } }) exports.checkEnv = key => hasOnly ? !!envs[key] : true exports.IS_WINDOWS = IS_WINDOWS exports.SHOULD_TEST_WINDOWS = process.env.IGNORE_TEST_WIN32 || IS_WINDOWS exports.debug = debug node-ignore-5.2.0/test/git-check-ignore.js000066400000000000000000000066511415752564400204050ustar00rootroot00000000000000const {test} = require('tap') const spawn = require('spawn-sync') const tmp = require('tmp').dirSync const mkdirp = require('mkdirp').sync const rm = require('rimraf').sync const fs = require('fs') const path = require('path') const {removeEnding} = require('pre-suf') const { debug, cases, checkEnv, IS_WINDOWS } = require('./fixtures/cases') const touch = (root, file, content) => { const dirs = file.split('/') const basename = dirs.pop() const dir = dirs.join('/') if (dir) { mkdirp(path.join(root, dir)) } // abc/ -> should not create file, but only dir if (basename) { fs.writeFileSync(path.join(root, file), content || '') } } const containsInOthers = (_path, index, paths) => { _path = removeEnding(_path, '/') return paths.some((p, i) => { if (index === i) { return false } return p === _path || p.indexOf(_path) === 0 && p[_path.length] === '/' }) } let tmpCount = 0 const tmpRoot = tmp().name const createUniqueTmp = () => { const dir = path.join(tmpRoot, String(tmpCount ++)) // Make sure the dir not exists, // clean up dirty things rm(dir) mkdirp(dir) return dir } const debugSpawn = (...args) => { const out = spawn(...args) debug(out.output.toString()) } const getNativeGitIgnoreResults = (rules, paths) => { const dir = createUniqueTmp() const gitignore = typeof rules === 'string' ? rules : rules.join('\n') touch(dir, '.gitignore', gitignore) paths.forEach((p, i) => { if (p === '.gitignore') { return } // We do not know if a path is NOT a file, // if we: // `touch a` // and then `touch a/b`, then boooom! if (containsInOthers(p, i, paths)) { return } touch(dir, p) }) spawn('git', ['init'], { cwd: dir }) spawn('git', ['add', '-A'], { cwd: dir }) debugSpawn('ls', ['-alF'], { cwd: dir }) debugSpawn('cat', ['.gitignore'], { cwd: dir }) return paths .filter(p => { let out = spawn('git', [ 'check-ignore', // `spawn` will escape the special cases for us p ], { cwd: dir }) .stdout .toString() // If a path has back slashes and is ignored by .gitignore, // the output of `git check-ignore` will contain // double quote pairs and CRLF // output: "b\\c/a.md" // -> string: 'b\\c.md' .replace(/\\\\/g, '\\') .replace(/^"?(.+?)"?(?:\r|\n)*$/g, (m, p1) => p1) out = removeEnding(out, '\n') debug('git check-ignore %s: %s -> ignored: %s', p, out, out === p) const ignored = out === p return !ignored }) } const notGitBuiltin = filename => filename.indexOf('.git/') !== 0 checkEnv('IGNORE_ONLY_FIXTURES') && cases(({ description, patterns, skip_test_fixture, paths, expected, expect_result }) => { if ( // In some platform, the behavior of git command about trailing spaces // is not implemented as documented, so skip testing skip_test_fixture // Tired to handle test cases for test cases for windows || IS_WINDOWS // `git check-ignore` could only handles non-empty filenames || !paths.some(Boolean) // `git check-ignore` will by default ignore .git/ directory // which `node-ignore` should not do as well || !expected.every(notGitBuiltin) ) { return } test(`test for test: ${description}`, t => { const result = getNativeGitIgnoreResults(patterns, paths).sort() expect_result(t, result) t.end() }) }) node-ignore-5.2.0/test/ignore.js000077500000000000000000000026001415752564400165420ustar00rootroot00000000000000const {test} = require('tap') const ignore = require('..') const { cases, checkEnv, SHOULD_TEST_WINDOWS } = require('./fixtures/cases') const make_win32 = path => path.replace(/\//g, '\\') cases(({ description, patterns, paths_object, paths, expect_result }) => { checkEnv('IGNORE_ONLY_FILTER') && test(`.filter(): ${description}`, t => { const ig = ignore() const result = ig .addPattern(patterns) .filter(paths) expect_result(t, result) t.end() }) checkEnv('IGNORE_ONLY_CREATE_FILTER') && test(`.createFilter(): ${description}`, t => { const result = paths.filter( ignore() .addPattern(patterns) .createFilter(), // thisArg should be binded null ) expect_result(t, result) t.end() }) checkEnv('IGNORE_ONLY_IGNORES') && test(`.ignores(path): ${description}`, t => { const ig = ignore().addPattern(patterns) Object.keys(paths_object).forEach(path => { t.is(ig.ignores(path), !!paths_object[path], `path: "${path}"`) }) t.end() }) if (!SHOULD_TEST_WINDOWS) { return } checkEnv('IGNORE_ONLY_WIN32') && test(`win32: .filter(): ${description}`, t => { const win_paths = paths.map(make_win32) const ig = ignore() const result = ig .addPattern(patterns) .filter(win_paths) expect_result(t, result, make_win32) t.end() }) }) node-ignore-5.2.0/test/others.js000066400000000000000000000102621415752564400165630ustar00rootroot00000000000000// - issues // - options // - static methods // - .test() const {test} = require('tap') const ignore = require('..') const { checkEnv, SHOULD_TEST_WINDOWS } = require('./fixtures/cases') const {isPathValid} = ignore const _test = checkEnv('IGNORE_ONLY_OTHERS') ? test : () => {} _test('.add()', t => { const a = ignore().add(['.abc/*', '!.abc/d/']) const b = ignore().add(a).add('!.abc/e/') const paths = [ '.abc/a.js', // filtered out '.abc/d/e.js', // included '.abc/e/e.js' // included by b, filtered out by a ] t.deepEqual(a.filter(paths), ['.abc/d/e.js']) t.deepEqual(b.filter(paths), ['.abc/d/e.js', '.abc/e/e.js']) t.end() }) _test('fixes babel class', t => { const {constructor} = ignore() try { constructor() } catch (e) { t.end() return } t.is('there should be an error', 'no error found') t.end() }) _test('#32', t => { const KEY_IGNORE = typeof Symbol !== 'undefined' ? Symbol.for('node-ignore') : 'node-ignore' const a = ignore().add(['.abc/*', '!.abc/d/']) // aa is actually not an IgnoreBase instance const aa = {} /* eslint no-underscore-dangle: ["off"] */ aa._rules = a._rules.slice() aa[KEY_IGNORE] = true const b = ignore().add(aa).add('!.abc/e/') const paths = [ '.abc/a.js', // filtered out '.abc/d/e.js', // included '.abc/e/e.js' // included by b, filtered out by a ] t.deepEqual(a.filter(paths), ['.abc/d/e.js']) t.deepEqual(b.filter(paths), ['.abc/d/e.js', '.abc/e/e.js']) t.end() }) _test('options.ignorecase', t => { const ig = ignore({ ignorecase: false }) ig.add('*.[jJ][pP]g') t.is(ig.ignores('a.jpg'), true) t.is(ig.ignores('a.JPg'), true) t.is(ig.ignores('a.JPG'), false) t.end() }) _test('special case: internal cache respects ignorecase', t => { const rule = '*.[jJ][pP]g' const ig = ignore({ ignorecase: false }) ig.add(rule) t.is(ig.ignores('a.JPG'), false) const ig2 = ignore({ ignorecase: true }) ig2.add(rule) t.is(ig2.ignores('a.JPG'), true) t.end() }) _test('special case: invalid paths, throw', t => { const ig = ignore() const emptyMessage = 'path must be a string, but got ""' t.throws(() => ig.ignores(''), emptyMessage) t.throws( () => ig.ignores(false), 'path must be a string, but got `false`' ) t.throws( () => ig.ignores('/a'), 'path must be `path.relative()`d, but got "/a"' ) if (SHOULD_TEST_WINDOWS) { t.throws( () => ig.ignores('c:\\a'), 'path must be `path.relative()`d, but got "c:\\a"' ) t.throws( () => ig.ignores('C:\\a'), 'path must be `path.relative()`d, but got "C:\\a"' ) } t.throws(() => ig.filter(['']), emptyMessage) t.throws(() => [''].filter(ig.createFilter()), emptyMessage) t.end() }) _test('isPathValid', t => { const paths = [ '.', './foo', '../foo', '/foo', false, 'foo' ] if (SHOULD_TEST_WINDOWS) { paths.push( '..\\foo', '.\\foo', '\\foo', '\\\\foo', 'C:\\foo', 'd:\\foo' ) } t.deepEqual( paths.filter(isPathValid), [ 'foo' ] ) t.end() }) const IGNORE_TEST_CASES = [ [ 'test: no rule', null, 'foo', [false, false] ], [ 'test: has rule, no match', 'bar', 'foo', [false, false] ], [ 'test: only negative', '!foo', 'foo', [false, true] ], [ 'test: ignored then unignored', ['foo', '!foo'], 'foo', [false, true] ], [ 'test: dir ignored then unignored -> not matched', ['foo', '!foo'], 'foo/bar', [false, false] ], [ 'test: ignored by wildcard, then unignored', ['*.js', '!a/a.js'], 'a/a.js', [false, true] ] ] IGNORE_TEST_CASES.forEach(([d, patterns, path, [ignored, unignored]]) => { _test(d, t => { const ig = ignore() if (patterns) { ig.add(patterns) } t.deepEqual(ig.test(path), { ignored, unignored }) t.end() }) }) _test('options.allowRelativePaths', t => { const ig = ignore({ allowRelativePaths: true }) ig.add('foo') t.is(ig.ignores('../foo/bar.js'), true) t.throws(() => ignore().ignores('../foo/bar.js')) t.end() }) node-ignore-5.2.0/test/ts/000077500000000000000000000000001415752564400153465ustar00rootroot00000000000000node-ignore-5.2.0/test/ts/simple.ts000066400000000000000000000025271415752564400172150ustar00rootroot00000000000000import ignore from '../../' const equal = (actual, expect, message) => { if (actual !== expect) { throw new Error(`${message}, expect: ${expect}, actual: ${actual}`) } } const paths = ['a', 'a/b', 'foo/bar'] let ig = ignore() ig = ig.add('*') ig = ig.add(['!*/', '!foo/bar']) const filter = ig.createFilter() paths.filter(filter) const passed: boolean = filter('a') equal(passed, false, 'filters a out') const filtered_paths: Array = ig.filter(paths) const ignores: boolean = ig.ignores('a') equal(ignores, true, 'ignores a') let ig2 = ignore() ig2 = ig2.add('# test ig.add(Ignore)') ig2 = ig2.add(ig) let ig3 = ignore() ig3 = ig3.add('*.js') let ig4 = ignore() ig4 = ig4.add('*.png') ig2 = ig2.add([ig3, ig4]) const ig5 = ignore({ ignorecase: false }) const isValid: boolean = ignore.isPathValid('./foo') equal(isValid, false, './foo is not valid') const { ignored, unignored }: { ignored: boolean, unignored: boolean } = ig4.test('foo') equal(ignored, false, 'not ignored') equal(unignored, false, 'not unignored') // Filter an Readyonly array const readonlyPaths = ['a', 'a/b', 'foo/bar'] as const ig.filter(readonlyPaths) // Add an Readonly array of rules const ig6 = ignore() ig6.add([ig3, ig4] as const) // options.ignoreCase and options.allowRelativePaths ignore({ ignoreCase: false, allowRelativePaths: true })