├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── appveyor.yml ├── benchmark ├── code │ ├── extglob.js │ └── minimatch.js ├── fixtures │ ├── negation-nested.js │ ├── negation-simple.js │ ├── range-false.js │ ├── range-true.js │ └── star-simple.js ├── index.js ├── package.json └── stats.md ├── changelog.md ├── examples ├── extglob.create.js ├── extglob.js ├── makeRe.js ├── parser.js ├── regex.js └── source-map.js ├── gulpfile.js ├── index.js ├── lib ├── compilers.js ├── extglob.js ├── parsers.js └── utils.js ├── package.json └── test ├── _negations.js ├── bash.extglob.js ├── bash.extglob1.js ├── bash.extglob1a.js ├── bash.extglob2.js ├── bash.extglob3.js ├── bash.js ├── capture.js ├── errors.js ├── fixtures └── posix.txt ├── options.js ├── reference.js ├── support ├── compare.js ├── match.js ├── matcher.js ├── parse.js ├── try-bash.js └── utils.js └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | 9 | "globals": { 10 | "document": false, 11 | "navigator": false, 12 | "window": false 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-spacing": [2, { "before": true, "after": true }], 18 | "block-spacing": [2, "always"], 19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 20 | "comma-dangle": [2, "never"], 21 | "comma-spacing": [2, { "before": false, "after": true }], 22 | "comma-style": [2, "last"], 23 | "constructor-super": 2, 24 | "curly": [2, "multi-line"], 25 | "dot-location": [2, "property"], 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "generator-star-spacing": [2, { "before": true, "after": true }], 29 | "handle-callback-err": [2, "^(err|error)$" ], 30 | "indent": [2, 2, { "SwitchCase": 1 }], 31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 32 | "keyword-spacing": [2, { "before": true, "after": true }], 33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 34 | "new-parens": 2, 35 | "no-array-constructor": 2, 36 | "no-caller": 2, 37 | "no-class-assign": 2, 38 | "no-cond-assign": 2, 39 | "no-const-assign": 2, 40 | "no-control-regex": 2, 41 | "no-debugger": 2, 42 | "no-delete-var": 2, 43 | "no-dupe-args": 2, 44 | "no-dupe-class-members": 2, 45 | "no-dupe-keys": 2, 46 | "no-duplicate-case": 2, 47 | "no-empty-character-class": 2, 48 | "no-eval": 2, 49 | "no-ex-assign": 2, 50 | "no-extend-native": 2, 51 | "no-extra-bind": 2, 52 | "no-extra-boolean-cast": 2, 53 | "no-extra-parens": [2, "functions"], 54 | "no-fallthrough": 2, 55 | "no-floating-decimal": 2, 56 | "no-func-assign": 2, 57 | "no-implied-eval": 2, 58 | "no-inner-declarations": [2, "functions"], 59 | "no-invalid-regexp": 2, 60 | "no-irregular-whitespace": 2, 61 | "no-iterator": 2, 62 | "no-label-var": 2, 63 | "no-labels": 2, 64 | "no-lone-blocks": 2, 65 | "no-mixed-spaces-and-tabs": 2, 66 | "no-multi-spaces": 2, 67 | "no-multi-str": 2, 68 | "no-multiple-empty-lines": [2, { "max": 1 }], 69 | "no-native-reassign": 0, 70 | "no-negated-in-lhs": 2, 71 | "no-new": 2, 72 | "no-new-func": 2, 73 | "no-new-object": 2, 74 | "no-new-require": 2, 75 | "no-new-wrappers": 2, 76 | "no-obj-calls": 2, 77 | "no-octal": 2, 78 | "no-octal-escape": 2, 79 | "no-proto": 0, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-return-assign": 2, 83 | "no-self-compare": 2, 84 | "no-sequences": 2, 85 | "no-shadow-restricted-names": 2, 86 | "no-spaced-func": 2, 87 | "no-sparse-arrays": 2, 88 | "no-this-before-super": 2, 89 | "no-throw-literal": 2, 90 | "no-trailing-spaces": 0, 91 | "no-undef": 2, 92 | "no-undef-init": 2, 93 | "no-unexpected-multiline": 2, 94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 95 | "no-unreachable": 2, 96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 97 | "no-useless-call": 0, 98 | "no-with": 2, 99 | "one-var": [0, { "initialized": "never" }], 100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 101 | "padded-blocks": [0, "never"], 102 | "quotes": [2, "single", "avoid-escape"], 103 | "radix": 2, 104 | "semi": [2, "always"], 105 | "semi-spacing": [2, { "before": false, "after": true }], 106 | "space-before-blocks": [2, "always"], 107 | "space-before-function-paren": [2, "never"], 108 | "space-in-parens": [2, "never"], 109 | "space-infix-ops": 2, 110 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 112 | "use-isnan": 2, 113 | "valid-typeof": 2, 114 | "wrap-iife": [2, "any"], 115 | "yoda": [2, "never"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | .idea 4 | .vscode 5 | *.sublime-* 6 | 7 | # test related, or directories generated by tests 8 | test/actual 9 | actual 10 | coverage 11 | .nyc* 12 | 13 | # npm 14 | node_modules 15 | npm-debug.log 16 | 17 | # yarn 18 | yarn.lock 19 | yarn-error.log 20 | 21 | # misc 22 | _gh_pages 23 | _draft 24 | _drafts 25 | bower_components 26 | vendor 27 | temp 28 | tmp 29 | TODO.md 30 | package-lock.json -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - '10' 8 | - '8' 9 | - '6' 10 | - '4' 11 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | - Convert an extglob string to a regex-compatible string. 2 | - More complete (and correct) support than [minimatch](https://github.com/isaacs/minimatch) (minimatch fails a large percentage of the extglob tests) 3 | - Handles [negation patterns](#extglob-patterns) 4 | - Handles [nested patterns](#extglob-patterns) 5 | - Organized code base, easy to maintain and make changes when edge cases arise 6 | - As you can see by the [benchmarks](#benchmarks), extglob doesn't pay with speed for it's completeness, accuracy and quality. 7 | 8 | **Heads up!**: This library only supports extglobs, to handle full glob patterns and other extended globbing features use [micromatch][] instead. 9 | 10 | ## Usage 11 | 12 | The main export is a function that takes a string and options, and returns an object with the parsed AST and the compiled `.output`, which is a regex-compatible string that can be used for matching. 13 | 14 | ```js 15 | var extglob = require('{%= name %}'); 16 | console.log(extglob('!(xyz)*.js')); 17 | ``` 18 | 19 | ## Extglob cheatsheet 20 | 21 | Extended globbing patterns can be defined as follows (as described by the [bash man page][bash]): 22 | 23 | | **pattern** | **regex equivalent** | **description** | 24 | | --- | --- | --- | 25 | | `?(pattern-list)` | `(...|...)?` | Matches zero or one occurrence of the given pattern(s) | 26 | | `*(pattern-list)` | `(...|...)*` | Matches zero or more occurrences of the given pattern(s) | 27 | | `+(pattern-list)` | `(...|...)+` | Matches one or more occurrences of the given pattern(s) | 28 | | `@(pattern-list)` | `(...|...)` [^1] | Matches one of the given pattern(s) | 29 | | `!(pattern-list)` | N/A | Matches anything except one of the given pattern(s) | 30 | 31 | [^1]: `@` isn't a RegEx character. 32 | 33 | ## API 34 | {%= apidocs("index.js") %} 35 | 36 | ## Options 37 | 38 | Available options are based on the options from Bash (and the option names used in bash). 39 | 40 | ### options.nullglob 41 | 42 | **Type**: `boolean` 43 | 44 | **Default**: `undefined` 45 | 46 | When enabled, the pattern itself will be returned when no matches are found. 47 | 48 | ### options.nonull 49 | 50 | Alias for [options.nullglob](#optionsnullglob), included for parity with minimatch. 51 | 52 | ### options.cache 53 | 54 | **Type**: `boolean` 55 | 56 | **Default**: `undefined` 57 | 58 | Functions are memoized based on the given glob patterns and options. Disable memoization by setting `options.cache` to false. 59 | 60 | ### options.failglob 61 | 62 | **Type**: `boolean` 63 | 64 | **Default**: `undefined` 65 | 66 | Throw an error is no matches are found. 67 | 68 | ## Benchmarks 69 | 70 | Last run on {%= date() %} 71 | 72 | ```sh 73 | {%= include("benchmark/stats.md") %} 74 | ``` 75 | 76 | ## Differences from Bash 77 | 78 | This library has complete parity with Bash 4.3 with only a couple of minor differences. 79 | 80 | - In some cases Bash returns true if the given string "contains" the pattern, whereas this library returns true if the string is an exact match for the pattern. You can relax this by setting `options.contains` to true. 81 | - This library is more accurate than Bash and thus does not fail some of the tests that Bash 4.3 still lists as failing in their unit tests 82 | 83 | [bash]: https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html 84 | [micromatch]: https://github.com/jonschlinkert/micromatch 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2018, Jon Schlinkert. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # extglob [![NPM version](https://img.shields.io/npm/v/extglob.svg?style=flat)](https://www.npmjs.com/package/extglob) [![NPM monthly downloads](https://img.shields.io/npm/dm/extglob.svg?style=flat)](https://npmjs.org/package/extglob) [![NPM total downloads](https://img.shields.io/npm/dt/extglob.svg?style=flat)](https://npmjs.org/package/extglob) [![Linux Build Status](https://img.shields.io/travis/micromatch/extglob.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/extglob) [![Windows Build Status](https://img.shields.io/appveyor/ci/micromatch/extglob.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/micromatch/extglob) 2 | 3 | > Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns. 4 | 5 | ## Install 6 | 7 | Install with [npm](https://www.npmjs.com/): 8 | 9 | ```sh 10 | $ npm install --save extglob 11 | ``` 12 | 13 | Install with [yarn](https://yarnpkg.com): 14 | 15 | ```sh 16 | $ yarn add extglob 17 | ``` 18 | 19 | * Convert an extglob string to a regex-compatible string. 20 | * More complete (and correct) support than [minimatch](https://github.com/isaacs/minimatch) (minimatch fails a large percentage of the extglob tests) 21 | * Handles [negation patterns](#extglob-patterns) 22 | * Handles [nested patterns](#extglob-patterns) 23 | * Organized code base, easy to maintain and make changes when edge cases arise 24 | * As you can see by the [benchmarks](#benchmarks), extglob doesn't pay with speed for it's completeness, accuracy and quality. 25 | 26 | **Heads up!**: This library only supports extglobs, to handle full glob patterns and other extended globbing features use [micromatch](https://github.com/jonschlinkert/micromatch) instead. 27 | 28 | ## Usage 29 | 30 | The main export is a function that takes a string and options, and returns an object with the parsed AST and the compiled `.output`, which is a regex-compatible string that can be used for matching. 31 | 32 | ```js 33 | var extglob = require('extglob'); 34 | console.log(extglob('!(xyz)*.js')); 35 | ``` 36 | 37 | ## Extglob cheatsheet 38 | 39 | Extended globbing patterns can be defined as follows (as described by the [bash man page](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html)): 40 | 41 | | **pattern** | **regex equivalent** | **description** | 42 | | --- | --- | --- | 43 | | `?(pattern-list)` | `(...\|...)?` | Matches zero or one occurrence of the given pattern(s) | 44 | | `*(pattern-list)` | `(...\|...)*` | Matches zero or more occurrences of the given pattern(s) | 45 | | `+(pattern-list)` | `(...\|...)+` | Matches one or more occurrences of the given pattern(s) | 46 | | `@(pattern-list)` | `(...\|...)` [^1] | Matches one of the given pattern(s) | 47 | | `!(pattern-list)` | N/A | Matches anything except one of the given pattern(s) | 48 | 49 | ## API 50 | 51 | ### [extglob](index.js#L35) 52 | 53 | Convert the given `extglob` pattern into a regex-compatible string. Returns an object with the compiled result and the parsed AST. 54 | 55 | **Params** 56 | 57 | * `pattern` **{String}** 58 | * `options` **{Object}** 59 | * `returns` **{String}** 60 | 61 | **Example** 62 | 63 | ```js 64 | const extglob = require('extglob'); 65 | console.log(extglob('*.!(*a)')); 66 | //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' 67 | ``` 68 | 69 | ### [.match](index.js#L55) 70 | 71 | Takes an array of strings and an extglob pattern and returns a new array that contains only the strings that match the pattern. 72 | 73 | **Params** 74 | 75 | * `list` **{Array}**: Array of strings to match 76 | * `pattern` **{String}**: Extglob pattern 77 | * `options` **{Object}** 78 | * `returns` **{Array}**: Returns an array of matches 79 | 80 | **Example** 81 | 82 | ```js 83 | const extglob = require('extglob'); 84 | console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)')); 85 | //=> ['a.b', 'a.c'] 86 | ``` 87 | 88 | ### [.isMatch](index.js#L110) 89 | 90 | Returns true if the specified `string` matches the given extglob `pattern`. 91 | 92 | **Params** 93 | 94 | * `string` **{String}**: String to match 95 | * `pattern` **{String}**: Extglob pattern 96 | * `options` **{String}** 97 | * `returns` **{Boolean}** 98 | 99 | **Example** 100 | 101 | ```js 102 | const extglob = require('extglob'); 103 | 104 | console.log(extglob.isMatch('a.a', '*.!(*a)')); 105 | //=> false 106 | console.log(extglob.isMatch('a.b', '*.!(*a)')); 107 | //=> true 108 | ``` 109 | 110 | ### [.contains](index.js#L149) 111 | 112 | Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but the pattern can match any part of the string. 113 | 114 | **Params** 115 | 116 | * `str` **{String}**: The string to match. 117 | * `pattern` **{String}**: Glob pattern to use for matching. 118 | * `options` **{Object}** 119 | * `returns` **{Boolean}**: Returns true if the patter matches any part of `str`. 120 | 121 | **Example** 122 | 123 | ```js 124 | const extglob = require('extglob'); 125 | console.log(extglob.contains('aa/bb/cc', '*b')); 126 | //=> true 127 | console.log(extglob.contains('aa/bb/cc', '*d')); 128 | //=> false 129 | ``` 130 | 131 | ### [.matcher](index.js#L183) 132 | 133 | Takes an extglob pattern and returns a matcher function. The returned function takes the string to match as its only argument. 134 | 135 | **Params** 136 | 137 | * `pattern` **{String}**: Extglob pattern 138 | * `options` **{String}** 139 | * `returns` **{Boolean}** 140 | 141 | **Example** 142 | 143 | ```js 144 | const extglob = require('extglob'); 145 | const isMatch = extglob.matcher('*.!(*a)'); 146 | 147 | console.log(isMatch('a.a')); 148 | //=> false 149 | console.log(isMatch('a.b')); 150 | //=> true 151 | ``` 152 | 153 | ### [.create](index.js#L213) 154 | 155 | Convert the given `extglob` pattern into a regex-compatible string. Returns an object with the compiled result and the parsed AST. 156 | 157 | **Params** 158 | 159 | * `str` **{String}** 160 | * `options` **{Object}** 161 | * `returns` **{String}** 162 | 163 | **Example** 164 | 165 | ```js 166 | const extglob = require('extglob'); 167 | console.log(extglob.create('*.!(*a)').output); 168 | //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' 169 | ``` 170 | 171 | ### [.capture](index.js#L247) 172 | 173 | Returns an array of matches captured by `pattern` in `string`, or `null` if the pattern did not match. 174 | 175 | **Params** 176 | 177 | * `pattern` **{String}**: Glob pattern to use for matching. 178 | * `string` **{String}**: String to match 179 | * `options` **{Object}**: See available [options](#options) for changing how matches are performed 180 | * `returns` **{Boolean}**: Returns an array of captures if the string matches the glob pattern, otherwise `null`. 181 | 182 | **Example** 183 | 184 | ```js 185 | const extglob = require('extglob'); 186 | extglob.capture(pattern, string[, options]); 187 | 188 | console.log(extglob.capture('test/*.js', 'test/foo.js')); 189 | //=> ['foo'] 190 | console.log(extglob.capture('test/*.js', 'foo/bar.css')); 191 | //=> null 192 | ``` 193 | 194 | ### [.makeRe](index.js#L280) 195 | 196 | Create a regular expression from the given `pattern` and `options`. 197 | 198 | **Params** 199 | 200 | * `pattern` **{String}**: The pattern to convert to regex. 201 | * `options` **{Object}** 202 | * `returns` **{RegExp}** 203 | 204 | **Example** 205 | 206 | ```js 207 | const extglob = require('extglob'); 208 | const re = extglob.makeRe('*.!(*a)'); 209 | console.log(re); 210 | //=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/ 211 | ``` 212 | 213 | ## Options 214 | 215 | Available options are based on the options from Bash (and the option names used in bash). 216 | 217 | ### options.nullglob 218 | 219 | **Type**: `boolean` 220 | 221 | **Default**: `undefined` 222 | 223 | When enabled, the pattern itself will be returned when no matches are found. 224 | 225 | ### options.nonull 226 | 227 | Alias for [options.nullglob](#optionsnullglob), included for parity with minimatch. 228 | 229 | ### options.cache 230 | 231 | **Type**: `boolean` 232 | 233 | **Default**: `undefined` 234 | 235 | Functions are memoized based on the given glob patterns and options. Disable memoization by setting `options.cache` to false. 236 | 237 | ### options.failglob 238 | 239 | **Type**: `boolean` 240 | 241 | **Default**: `undefined` 242 | 243 | Throw an error is no matches are found. 244 | 245 | ## Benchmarks 246 | 247 | Last run on April 30, 2018 248 | 249 | ```sh 250 | # negation-nested (49 bytes) 251 | extglob x 1,380,148 ops/sec ±3.35% (62 runs sampled) 252 | minimatch x 156,800 ops/sec ±4.13% (76 runs sampled) 253 | 254 | fastest is extglob (by 880% avg) 255 | 256 | # negation-simple (43 bytes) 257 | extglob x 1,821,746 ops/sec ±1.61% (76 runs sampled) 258 | minimatch x 365,618 ops/sec ±1.87% (84 runs sampled) 259 | 260 | fastest is extglob (by 498% avg) 261 | 262 | # range-false (57 bytes) 263 | extglob x 2,038,592 ops/sec ±3.39% (85 runs sampled) 264 | minimatch x 310,897 ops/sec ±12.62% (87 runs sampled) 265 | 266 | fastest is extglob (by 656% avg) 267 | 268 | # range-true (56 bytes) 269 | extglob x 2,105,081 ops/sec ±0.69% (91 runs sampled) 270 | minimatch x 332,188 ops/sec ±0.45% (91 runs sampled) 271 | 272 | fastest is extglob (by 634% avg) 273 | 274 | # star-simple (46 bytes) 275 | extglob x 2,154,184 ops/sec ±0.99% (89 runs sampled) 276 | minimatch x 452,812 ops/sec ±0.51% (88 runs sampled) 277 | 278 | fastest is extglob (by 476% avg) 279 | 280 | ``` 281 | 282 | ## Differences from Bash 283 | 284 | This library has complete parity with Bash 4.3 with only a couple of minor differences. 285 | 286 | * In some cases Bash returns true if the given string "contains" the pattern, whereas this library returns true if the string is an exact match for the pattern. You can relax this by setting `options.contains` to true. 287 | * This library is more accurate than Bash and thus does not fail some of the tests that Bash 4.3 still lists as failing in their unit tests 288 | 289 | ## About 290 | 291 | ### Related projects 292 | 293 | * [braces](https://www.npmjs.com/package/braces): Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support… [more](https://github.com/micromatch/braces) | [homepage](https://github.com/micromatch/braces "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.") 294 | * [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/micromatch/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.") 295 | * [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. Used… [more](https://github.com/jonschlinkert/expand-range) | [homepage](https://github.com/jonschlinkert/expand-range "Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. Used by [micromatch].") 296 | * [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`") 297 | * [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.") 298 | 299 | ### Contributing 300 | 301 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 302 | 303 | ### Contributors 304 | 305 | | **Commits** | **Contributor** | 306 | | --- | --- | 307 | | 54 | [jonschlinkert](https://github.com/jonschlinkert) | 308 | | 6 | [danez](https://github.com/danez) | 309 | | 2 | [isiahmeadows](https://github.com/isiahmeadows) | 310 | | 1 | [doowb](https://github.com/doowb) | 311 | | 1 | [devongovett](https://github.com/devongovett) | 312 | | 1 | [mjbvz](https://github.com/mjbvz) | 313 | | 1 | [shinnn](https://github.com/shinnn) | 314 | 315 | ### Building docs 316 | 317 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 318 | 319 | To generate the readme, run the following command: 320 | 321 | ```sh 322 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 323 | ``` 324 | 325 | ### Running tests 326 | 327 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 328 | 329 | ```sh 330 | $ npm install && npm test 331 | ``` 332 | 333 | ### Author 334 | 335 | **Jon Schlinkert** 336 | 337 | * [github/jonschlinkert](https://github.com/jonschlinkert) 338 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 339 | 340 | ### License 341 | 342 | Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). 343 | Released under the [MIT License](LICENSE). 344 | 345 | *** 346 | 347 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on April 30, 2018._ -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Test against this version of Node.js 2 | environment: 3 | matrix: 4 | # node.js 5 | - nodejs_version: "10.0" 6 | - nodejs_version: "8.0" 7 | - nodejs_version: "6.0" 8 | - nodejs_version: "4.0" 9 | 10 | # Install scripts. (runs after repo cloning) 11 | install: 12 | # Get the latest stable version of Node.js or io.js 13 | - ps: Install-Product node $env:nodejs_version 14 | # install modules 15 | - npm install 16 | 17 | # Post-install test scripts. 18 | test_script: 19 | # Output useful info for debugging. 20 | - node --version 21 | - npm --version 22 | # run tests 23 | - npm test 24 | 25 | # Don't actually build. 26 | build: off 27 | -------------------------------------------------------------------------------- /benchmark/code/extglob.js: -------------------------------------------------------------------------------- 1 | var extglob = require('../..'); 2 | 3 | module.exports = function(str, pattern) { 4 | return extglob.isMatch(str, pattern); 5 | }; 6 | -------------------------------------------------------------------------------- /benchmark/code/minimatch.js: -------------------------------------------------------------------------------- 1 | var minimatch = require('minimatch'); 2 | 3 | module.exports = function(str, pattern) { 4 | return minimatch(str, pattern); 5 | }; 6 | -------------------------------------------------------------------------------- /benchmark/fixtures/negation-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = ['a.b', 'a.!(!(@(*b)))', true]; 2 | -------------------------------------------------------------------------------- /benchmark/fixtures/negation-simple.js: -------------------------------------------------------------------------------- 1 | module.exports = ['a.b', 'a.!(*a)', true]; 2 | -------------------------------------------------------------------------------- /benchmark/fixtures/range-false.js: -------------------------------------------------------------------------------- 1 | module.exports = ['para381', 'para?([345]|99)1', false]; 2 | -------------------------------------------------------------------------------- /benchmark/fixtures/range-true.js: -------------------------------------------------------------------------------- 1 | module.exports = ['para991', 'para?([345]|99)1', true]; 2 | -------------------------------------------------------------------------------- /benchmark/fixtures/star-simple.js: -------------------------------------------------------------------------------- 1 | module.exports = ['abef', 'ab*(e|f)*', true]; 2 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const suite = require('benchmarked'); 5 | const write = require('write'); 6 | 7 | suite.run({code: 'code/*.js', fixtures: 'fixtures/*.js'}) 8 | .then(function(stats) { 9 | return write(path.join(__dirname, 'stats.md'), suite.render(stats)); 10 | }) 11 | .catch(console.error); 12 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extglob-benchmarks", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "index.js", 6 | "dependencies": { 7 | "benchmarked": "^2.0.0", 8 | "write": "^1.0.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /benchmark/stats.md: -------------------------------------------------------------------------------- 1 | # negation-nested (49 bytes) 2 | extglob x 1,380,148 ops/sec ±3.35% (62 runs sampled) 3 | minimatch x 156,800 ops/sec ±4.13% (76 runs sampled) 4 | 5 | fastest is extglob (by 880% avg) 6 | 7 | # negation-simple (43 bytes) 8 | extglob x 1,821,746 ops/sec ±1.61% (76 runs sampled) 9 | minimatch x 365,618 ops/sec ±1.87% (84 runs sampled) 10 | 11 | fastest is extglob (by 498% avg) 12 | 13 | # range-false (57 bytes) 14 | extglob x 2,038,592 ops/sec ±3.39% (85 runs sampled) 15 | minimatch x 310,897 ops/sec ±12.62% (87 runs sampled) 16 | 17 | fastest is extglob (by 656% avg) 18 | 19 | # range-true (56 bytes) 20 | extglob x 2,105,081 ops/sec ±0.69% (91 runs sampled) 21 | minimatch x 332,188 ops/sec ±0.45% (91 runs sampled) 22 | 23 | fastest is extglob (by 634% avg) 24 | 25 | # star-simple (46 bytes) 26 | extglob x 2,154,184 ops/sec ±0.99% (89 runs sampled) 27 | minimatch x 452,812 ops/sec ±0.51% (88 runs sampled) 28 | 29 | fastest is extglob (by 476% avg) 30 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | ### v3.0.0 4 | 5 | **Breaking changes** 6 | 7 | - Snapdragon was updated to 0.12. Other packages that integrate `extglob` need to also use snapdragon 0.12. 8 | - Minimum Node.JS version is now version 4. 9 | 10 | 11 | ### v2.0.0 12 | 13 | **Added features** 14 | 15 | - Adds [.capture](readme.md#capture) method for capturing matches, thanks to [devongovett](https://github.com/devongovett) 16 | 17 | 18 | ### v1.0.0 19 | 20 | **Breaking changes** 21 | 22 | - The main export now returns the compiled string, instead of the object returned from the compiler 23 | 24 | **Added features** 25 | 26 | - Adds a `.create` method to do what the main function did before v1.0.0 27 | 28 | **Other changes** 29 | 30 | - adds `expand-brackets` parsers/compilers to handle nested brackets and extglobs 31 | - uses `to-regex` to build regex for `makeRe` method 32 | - improves coverage 33 | - optimizations -------------------------------------------------------------------------------- /examples/extglob.create.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extglob = require('..'); 4 | 5 | console.log(extglob.create('*.!(*a)')); 6 | console.log(extglob.create('*(*(of*(a)x)z)')); 7 | 8 | -------------------------------------------------------------------------------- /examples/extglob.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extglob = require('..'); 4 | 5 | console.log(extglob('!(xyz)*.js')); 6 | //=>'(?:(?!(?:xyz)).*?)(?!\.).*?\.js' 7 | 8 | console.log(extglob('*.!(*a)')); 9 | //=> '(?!\.).*?\.(?:(?!(?:(?!\.).*?a$)).*?)' 10 | 11 | console.log(extglob('+(*(of*(a)x)z)')); 12 | //=> '+((of(a)*x)*z)*' 13 | -------------------------------------------------------------------------------- /examples/makeRe.js: -------------------------------------------------------------------------------- 1 | // var mm = require('minimatch'); 2 | var extglob = require('./'); 3 | 4 | // var re = /^(?!a|b)[^/]*?\.md$/; 5 | var re = extglob.makeRe('!(?:(a|b)*).md'); 6 | // var re = mm.makeRe('!(a|b)*.md'); 7 | var arr = ['a.js', 'a.md', 'b.md', 'c.md'] 8 | .filter(function(ele) { 9 | return re.test(ele); 10 | }); 11 | console.log(arr); 12 | -------------------------------------------------------------------------------- /examples/parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var parsers = require('../lib/parsers'); 4 | var Extglob = require('../lib/extglob'); 5 | var extglob = new Extglob(); 6 | extglob.use(parsers); 7 | 8 | var pattern = '*(*(of*(a)x)z)'; 9 | var res = extglob.parse(pattern); 10 | console.log(res); 11 | -------------------------------------------------------------------------------- /examples/regex.js: -------------------------------------------------------------------------------- 1 | 2 | var extglob = require('..'); 3 | 4 | var re = extglob.makeRe('*(a|b|c)'); 5 | console.log(re); 6 | console.log(re.test('bar')); 7 | console.log(re.test('bbbaaaccc')); 8 | -------------------------------------------------------------------------------- /examples/source-map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extglob = require('..'); 4 | var pattern = '*(*(of*(a)x)z)'; 5 | 6 | console.log(extglob.create(pattern)); 7 | console.log(extglob.create(pattern, {sourcemap: true})); 8 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('gulp'); 4 | const mocha = require('gulp-mocha'); 5 | const unused = require('gulp-unused'); 6 | const istanbul = require('gulp-istanbul'); 7 | const eslint = require('gulp-eslint'); 8 | 9 | gulp.task('coverage', function() { 10 | return gulp.src(['index.js', 'lib/*.js']) 11 | .pipe(istanbul()) 12 | .pipe(istanbul.hookRequire()); 13 | }); 14 | 15 | gulp.task('test', ['coverage'], function() { 16 | return gulp.src('test/*.js') 17 | .pipe(mocha({reporter: 'spec'})) 18 | .pipe(istanbul.writeReports()); 19 | }); 20 | 21 | gulp.task('lint', function() { 22 | return gulp.src(['*.js', 'lib/*.js', 'test/{*,support/*}.js']) 23 | .pipe(eslint()) 24 | .pipe(eslint.format()); 25 | }); 26 | 27 | gulp.task('unused', function() { 28 | return gulp.src(['index.js', 'lib/*.js']) 29 | .pipe(unused({keys: Object.keys(require('./lib/utils.js'))})); 30 | }); 31 | 32 | gulp.task('default', ['test', 'lint']); 33 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | 7 | const unique = require('array-unique'); 8 | const toRegex = require('to-regex'); 9 | 10 | /** 11 | * Local dependencies 12 | */ 13 | 14 | const compilers = require('./lib/compilers'); 15 | const parsers = require('./lib/parsers'); 16 | const Extglob = require('./lib/extglob'); 17 | const utils = require('./lib/utils'); 18 | const MAX_LENGTH = 1024 * 64; 19 | 20 | /** 21 | * Convert the given `extglob` pattern into a regex-compatible string. Returns 22 | * an object with the compiled result and the parsed AST. 23 | * 24 | * ```js 25 | * const extglob = require('extglob'); 26 | * console.log(extglob('*.!(*a)')); 27 | * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' 28 | * ``` 29 | * @param {String} `pattern` 30 | * @param {Object} `options` 31 | * @return {String} 32 | * @api public 33 | */ 34 | 35 | function extglob(pattern, options) { 36 | return extglob.create(pattern, options).output; 37 | } 38 | 39 | /** 40 | * Takes an array of strings and an extglob pattern and returns a new 41 | * array that contains only the strings that match the pattern. 42 | * 43 | * ```js 44 | * const extglob = require('extglob'); 45 | * console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)')); 46 | * //=> ['a.b', 'a.c'] 47 | * ``` 48 | * @param {Array} `list` Array of strings to match 49 | * @param {String} `pattern` Extglob pattern 50 | * @param {Object} `options` 51 | * @return {Array} Returns an array of matches 52 | * @api public 53 | */ 54 | 55 | extglob.match = function(list, pattern, options) { 56 | if (typeof pattern !== 'string') { 57 | throw new TypeError('expected pattern to be a string'); 58 | } 59 | 60 | list = utils.arrayify(list); 61 | const isMatch = extglob.matcher(pattern, options); 62 | const len = list.length; 63 | let idx = -1; 64 | const matches = []; 65 | 66 | while (++idx < len) { 67 | const ele = list[idx]; 68 | 69 | if (isMatch(ele)) { 70 | matches.push(ele); 71 | } 72 | } 73 | 74 | // if no options were passed, uniquify results and return 75 | if (typeof options === 'undefined') { 76 | return unique(matches); 77 | } 78 | 79 | if (matches.length === 0) { 80 | if (options.failglob === true) { 81 | throw new Error('no matches found for "' + pattern + '"'); 82 | } 83 | if (options.nonull === true || options.nullglob === true) { 84 | return [pattern.split('\\').join('')]; 85 | } 86 | } 87 | 88 | return options.nodupes !== false ? unique(matches) : matches; 89 | }; 90 | 91 | /** 92 | * Returns true if the specified `string` matches the given 93 | * extglob `pattern`. 94 | * 95 | * ```js 96 | * const extglob = require('extglob'); 97 | * 98 | * console.log(extglob.isMatch('a.a', '*.!(*a)')); 99 | * //=> false 100 | * console.log(extglob.isMatch('a.b', '*.!(*a)')); 101 | * //=> true 102 | * ``` 103 | * @param {String} `string` String to match 104 | * @param {String} `pattern` Extglob pattern 105 | * @param {String} `options` 106 | * @return {Boolean} 107 | * @api public 108 | */ 109 | 110 | extglob.isMatch = function(str, pattern, options) { 111 | if (typeof pattern !== 'string') { 112 | throw new TypeError('expected pattern to be a string'); 113 | } 114 | 115 | if (typeof str !== 'string') { 116 | throw new TypeError('expected a string'); 117 | } 118 | 119 | if (pattern === str) { 120 | return true; 121 | } 122 | 123 | if (pattern === '' || pattern === ' ' || pattern === '.') { 124 | return pattern === str; 125 | } 126 | 127 | const isMatch = utils.memoize('isMatch', pattern, options, extglob.matcher); 128 | return isMatch(str); 129 | }; 130 | 131 | /** 132 | * Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but 133 | * the pattern can match any part of the string. 134 | * 135 | * ```js 136 | * const extglob = require('extglob'); 137 | * console.log(extglob.contains('aa/bb/cc', '*b')); 138 | * //=> true 139 | * console.log(extglob.contains('aa/bb/cc', '*d')); 140 | * //=> false 141 | * ``` 142 | * @param {String} `str` The string to match. 143 | * @param {String} `pattern` Glob pattern to use for matching. 144 | * @param {Object} `options` 145 | * @return {Boolean} Returns true if the patter matches any part of `str`. 146 | * @api public 147 | */ 148 | 149 | extglob.contains = function(str, pattern, options) { 150 | if (typeof str !== 'string') { 151 | throw new TypeError('expected a string'); 152 | } 153 | 154 | if (pattern === '' || pattern === ' ' || pattern === '.') { 155 | return pattern === str; 156 | } 157 | 158 | const opts = Object.assign({}, options, {contains: true}); 159 | opts.strictClose = false; 160 | opts.strictOpen = false; 161 | return extglob.isMatch(str, pattern, opts); 162 | }; 163 | 164 | /** 165 | * Takes an extglob pattern and returns a matcher function. The returned 166 | * function takes the string to match as its only argument. 167 | * 168 | * ```js 169 | * const extglob = require('extglob'); 170 | * const isMatch = extglob.matcher('*.!(*a)'); 171 | * 172 | * console.log(isMatch('a.a')); 173 | * //=> false 174 | * console.log(isMatch('a.b')); 175 | * //=> true 176 | * ``` 177 | * @param {String} `pattern` Extglob pattern 178 | * @param {String} `options` 179 | * @return {Boolean} 180 | * @api public 181 | */ 182 | 183 | extglob.matcher = function(pattern, options) { 184 | if (typeof pattern !== 'string') { 185 | throw new TypeError('expected pattern to be a string'); 186 | } 187 | 188 | function matcher() { 189 | const re = extglob.makeRe(pattern, options); 190 | return function(str) { 191 | return re.test(str); 192 | }; 193 | } 194 | 195 | return utils.memoize('matcher', pattern, options, matcher); 196 | }; 197 | 198 | /** 199 | * Convert the given `extglob` pattern into a regex-compatible string. Returns 200 | * an object with the compiled result and the parsed AST. 201 | * 202 | * ```js 203 | * const extglob = require('extglob'); 204 | * console.log(extglob.create('*.!(*a)').output); 205 | * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' 206 | * ``` 207 | * @param {String} `str` 208 | * @param {Object} `options` 209 | * @return {String} 210 | * @api public 211 | */ 212 | 213 | extglob.create = function(pattern, options) { 214 | if (typeof pattern !== 'string') { 215 | throw new TypeError('expected pattern to be a string'); 216 | } 217 | 218 | function create() { 219 | const ext = new Extglob(options); 220 | const ast = ext.parse(pattern, options); 221 | return ext.compile(ast, options); 222 | } 223 | 224 | return utils.memoize('create', pattern, options, create); 225 | }; 226 | 227 | /** 228 | * Returns an array of matches captured by `pattern` in `string`, or `null` 229 | * if the pattern did not match. 230 | * 231 | * ```js 232 | * const extglob = require('extglob'); 233 | * extglob.capture(pattern, string[, options]); 234 | * 235 | * console.log(extglob.capture('test/*.js', 'test/foo.js')); 236 | * //=> ['foo'] 237 | * console.log(extglob.capture('test/*.js', 'foo/bar.css')); 238 | * //=> null 239 | * ``` 240 | * @param {String} `pattern` Glob pattern to use for matching. 241 | * @param {String} `string` String to match 242 | * @param {Object} `options` See available [options](#options) for changing how matches are performed 243 | * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. 244 | * @api public 245 | */ 246 | 247 | extglob.capture = function(pattern, str, options) { 248 | const re = extglob.makeRe(pattern, Object.assign({capture: true}, options)); 249 | 250 | function match() { 251 | return function(string) { 252 | const match = re.exec(string); 253 | if (!match) { 254 | return null; 255 | } 256 | 257 | return match.slice(1); 258 | }; 259 | } 260 | 261 | const capture = utils.memoize('capture', pattern, options, match); 262 | return capture(str); 263 | }; 264 | 265 | /** 266 | * Create a regular expression from the given `pattern` and `options`. 267 | * 268 | * ```js 269 | * const extglob = require('extglob'); 270 | * const re = extglob.makeRe('*.!(*a)'); 271 | * console.log(re); 272 | * //=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/ 273 | * ``` 274 | * @param {String} `pattern` The pattern to convert to regex. 275 | * @param {Object} `options` 276 | * @return {RegExp} 277 | * @api public 278 | */ 279 | 280 | extglob.makeRe = function(pattern, options) { 281 | if (pattern instanceof RegExp) { 282 | return pattern; 283 | } 284 | 285 | if (typeof pattern !== 'string') { 286 | throw new TypeError('expected pattern to be a string'); 287 | } 288 | 289 | if (pattern.length > MAX_LENGTH) { 290 | throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); 291 | } 292 | 293 | function makeRe() { 294 | const opts = Object.assign({strictErrors: false}, options); 295 | if (opts.strictErrors === true) opts.strict = true; 296 | const res = extglob.create(pattern, opts); 297 | return toRegex(res.output, opts); 298 | } 299 | 300 | const regex = utils.memoize('makeRe', pattern, options, makeRe); 301 | if (regex.source.length > MAX_LENGTH) { 302 | throw new SyntaxError('potentially malicious regex detected'); 303 | } 304 | 305 | return regex; 306 | }; 307 | 308 | /** 309 | * Cache 310 | */ 311 | 312 | extglob.cache = utils.cache; 313 | extglob.clearCache = function() { 314 | extglob.cache.__data__ = {}; 315 | }; 316 | 317 | /** 318 | * Expose `Extglob` constructor, parsers and compilers 319 | */ 320 | 321 | extglob.Extglob = Extglob; 322 | extglob.compilers = compilers; 323 | extglob.parsers = parsers; 324 | 325 | /** 326 | * Expose `extglob` 327 | * @type {Function} 328 | */ 329 | 330 | module.exports = extglob; 331 | -------------------------------------------------------------------------------- /lib/compilers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const brackets = require('expand-brackets'); 4 | 5 | /** 6 | * Extglob compilers 7 | */ 8 | 9 | module.exports = function(extglob) { 10 | function star() { 11 | if (typeof extglob.options.star === 'function') { 12 | return extglob.options.star.apply(this, arguments); 13 | } 14 | if (typeof extglob.options.star === 'string') { 15 | return extglob.options.star; 16 | } 17 | return '.*?'; 18 | } 19 | 20 | /** 21 | * Use `expand-brackets` compilers 22 | */ 23 | 24 | extglob.use(brackets.compilers); 25 | extglob.compiler 26 | 27 | /** 28 | * Escaped: "\\*" 29 | */ 30 | 31 | .set('escape', function(node) { 32 | return this.emit(node.val, node); 33 | }) 34 | 35 | /** 36 | * Dot: "." 37 | */ 38 | 39 | .set('dot', function(node) { 40 | return this.emit('\\' + node.val, node); 41 | }) 42 | 43 | /** 44 | * Question mark: "?" 45 | */ 46 | 47 | .set('qmark', function(node) { 48 | let val = '[^\\\\/.]'; 49 | const prev = node.prev; 50 | 51 | if (node.parsed.slice(-1) === '(') { 52 | const ch = node.rest.charAt(0); 53 | if (ch !== '!' && ch !== '=' && ch !== ':') { 54 | return this.emit(val, node); 55 | } 56 | return this.emit(node.val, node); 57 | } 58 | 59 | if (prev.type === 'text' && prev.val) { 60 | return this.emit(val, node); 61 | } 62 | 63 | if (node.val.length > 1) { 64 | val += '{' + node.val.length + '}'; 65 | } 66 | return this.emit(val, node); 67 | }) 68 | 69 | /** 70 | * Plus: "+" 71 | */ 72 | 73 | .set('plus', function(node) { 74 | const prev = node.parsed.slice(-1); 75 | if (prev === ']' || prev === ')') { 76 | return this.emit(node.val, node); 77 | } 78 | const ch = this.output.slice(-1); 79 | if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) { 80 | return this.emit('\\+', node); 81 | } 82 | if (/\w/.test(ch) && !node.inside) { 83 | return this.emit('+\\+?', node); 84 | } 85 | return this.emit('+', node); 86 | }) 87 | 88 | /** 89 | * Star: "*" 90 | */ 91 | 92 | .set('star', function(node) { 93 | const prev = node.prev; 94 | const prefix = prev.type !== 'text' && prev.type !== 'escape' 95 | ? '(?!\\.)' 96 | : ''; 97 | 98 | return this.emit(prefix + star.call(this, node), node); 99 | }) 100 | 101 | /** 102 | * Parens 103 | */ 104 | 105 | .set('paren', function(node) { 106 | this.mapVisit(node); 107 | }) 108 | .set('paren.open', function(node) { 109 | const capture = this.options.capture ? '(' : ''; 110 | 111 | switch (node.parent.prefix) { 112 | case '!': 113 | case '^': 114 | return this.emit(capture + '(?:(?!(?:', node); 115 | case '*': 116 | case '+': 117 | case '?': 118 | case '@': 119 | return this.emit(capture + '(?:', node); 120 | default: { 121 | let val = node.val; 122 | if (this.options.bash === true) { 123 | val = '\\' + val; 124 | } else if (!this.options.capture && val === '(' && node.parent.rest[0] !== '?') { 125 | val += '?:'; 126 | } 127 | 128 | return this.emit(val, node); 129 | } 130 | } 131 | }) 132 | .set('paren.close', function(node) { 133 | const capture = this.options.capture ? ')' : ''; 134 | 135 | switch (node.prefix) { 136 | case '!': 137 | case '^': 138 | const prefix = /^(\)|$)/.test(node.rest) ? '$' : ''; 139 | let str = star.call(this, node); 140 | 141 | // if the extglob has a slash explicitly defined, we know the user wants 142 | // to match slashes, so we need to ensure the "star" regex allows for it 143 | if (node.parent.hasSlash && !this.options.star && this.options.slash !== false) { 144 | str = '.*?'; 145 | } 146 | 147 | return this.emit(prefix + ('))' + str + ')') + capture, node); 148 | case '*': 149 | case '+': 150 | case '?': 151 | return this.emit(')' + node.prefix + capture, node); 152 | case '@': 153 | return this.emit(')' + capture, node); 154 | default: { 155 | const val = (this.options.bash === true ? '\\' : '') + ')'; 156 | return this.emit(val, node); 157 | } 158 | } 159 | }) 160 | 161 | /** 162 | * Text 163 | */ 164 | 165 | .set('text', function(node) { 166 | const val = node.val.replace(/[\[\]]/g, '\\$&'); 167 | return this.emit(val, node); 168 | }); 169 | }; 170 | -------------------------------------------------------------------------------- /lib/extglob.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | 7 | const Snapdragon = require('snapdragon'); 8 | const capture = require('snapdragon-capture'); 9 | const define = require('define-property'); 10 | 11 | /** 12 | * Local dependencies 13 | */ 14 | 15 | const compilers = require('./compilers'); 16 | const parsers = require('./parsers'); 17 | 18 | /** 19 | * Customize Snapdragon parser and renderer 20 | */ 21 | 22 | function Extglob(options) { 23 | this.options = Object.assign({source: 'extglob'}, options); 24 | this.snapdragon = this.options.snapdragon || new Snapdragon(this.options); 25 | this.snapdragon.use(capture()); 26 | this.snapdragon.patterns = this.snapdragon.patterns || {}; 27 | this.compiler = this.snapdragon.compiler; 28 | this.parser = this.snapdragon.parser; 29 | 30 | compilers(this.snapdragon); 31 | parsers(this.snapdragon); 32 | 33 | /** 34 | * Override Snapdragon `.parse` method 35 | */ 36 | 37 | define(this.snapdragon, 'parse', function(str, options) { 38 | const parsed = Snapdragon.prototype.parse.apply(this, arguments); 39 | parsed.input = str; 40 | 41 | // escape unmatched brace/bracket/parens 42 | const last = this.parser.stack.pop(); 43 | if (last && this.options.strict !== true) { 44 | const node = last.nodes[0]; 45 | node.val = '\\' + node.val; 46 | const sibling = node.parent.nodes[1]; 47 | if (sibling.type === 'star') { 48 | sibling.loose = true; 49 | } 50 | } 51 | 52 | // add non-enumerable parser reference 53 | define(parsed, 'parser', this.parser); 54 | return parsed; 55 | }); 56 | 57 | /** 58 | * Decorate `.parse` method 59 | */ 60 | 61 | define(this, 'parse', function(ast, options) { 62 | return this.snapdragon.parse.apply(this.snapdragon, arguments); 63 | }); 64 | 65 | /** 66 | * Decorate `.compile` method 67 | */ 68 | 69 | define(this, 'compile', function(ast, options) { 70 | return this.snapdragon.compile.apply(this.snapdragon, arguments); 71 | }); 72 | 73 | } 74 | 75 | /** 76 | * Expose `Extglob` 77 | */ 78 | 79 | module.exports = Extglob; 80 | -------------------------------------------------------------------------------- /lib/parsers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const brackets = require('expand-brackets'); 4 | const utils = require('./utils'); 5 | 6 | /** 7 | * Characters to use in text regex (we want to "not" match 8 | * characters that are matched by other parsers) 9 | */ 10 | 11 | const TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+'; 12 | const not = utils.createRegex(TEXT_REGEX); 13 | 14 | /** 15 | * Extglob parsers 16 | */ 17 | 18 | function parsers(extglob) { 19 | extglob.state = extglob.state || {}; 20 | 21 | /** 22 | * Use `expand-brackets` parsers 23 | */ 24 | 25 | extglob.use(brackets.parsers); 26 | extglob.parser.sets.paren = extglob.parser.sets.paren || []; 27 | extglob.parser 28 | 29 | /** 30 | * Extglob open: "*(" 31 | */ 32 | 33 | .set('paren.open', function() { 34 | const pos = this.position(); 35 | const m = this.match(/^([!@*?+])?\(/); 36 | if (!m) return; 37 | 38 | const prev = this.prev(); 39 | const prefix = m[1]; 40 | const val = m[0]; 41 | 42 | const open = pos({ 43 | type: 'paren.open', 44 | val 45 | }); 46 | 47 | const node = pos({ 48 | type: 'paren', 49 | prefix, 50 | nodes: [] 51 | }); 52 | 53 | // if nested negation extglobs, just cancel them out to simplify 54 | if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') { 55 | prev.prefix = '@'; 56 | node.prefix = '@'; 57 | } 58 | 59 | this.pushNode(node, prev); 60 | this.pushNode(open, node); 61 | this.push('paren', node); 62 | }) 63 | 64 | /** 65 | * Extglob close: ")" 66 | */ 67 | 68 | .set('paren.close', function() { 69 | const parsed = this.parsed; 70 | const pos = this.position(); 71 | const m = this.match(/^\)/); 72 | if (!m) return; 73 | 74 | const parent = this.pop('paren'); 75 | const node = pos({ 76 | type: 'paren.close', 77 | rest: this.input, 78 | parsed, 79 | val: m[0] 80 | }); 81 | 82 | if (!this.isType(parent, 'paren')) { 83 | if (this.options.strict) { 84 | throw new Error('missing opening paren: "("'); 85 | } 86 | node.escaped = true; 87 | return node; 88 | } 89 | 90 | node.prefix = parent.prefix; 91 | this.pushNode(node, parent); 92 | }) 93 | 94 | /** 95 | * Escape: "\\." 96 | */ 97 | 98 | .set('escape', function() { 99 | const pos = this.position(); 100 | const m = this.match(/^\\(.)/); 101 | if (!m) return; 102 | 103 | return pos({ 104 | type: 'escape', 105 | val: m[0], 106 | ch: m[1] 107 | }); 108 | }) 109 | 110 | /** 111 | * Question marks: "?" 112 | */ 113 | 114 | .set('qmark', function() { 115 | const parsed = this.parsed; 116 | const pos = this.position(); 117 | const m = this.match(/^\?+(?!\()/); 118 | if (!m) return; 119 | extglob.state.metachar = true; 120 | return pos({ 121 | type: 'qmark', 122 | rest: this.input, 123 | parsed, 124 | val: m[0] 125 | }); 126 | }) 127 | 128 | /** 129 | * Character parsers 130 | */ 131 | 132 | .capture('star', /^\*(?!\()/) 133 | .capture('plus', /^\+(?!\()/) 134 | .capture('dot', /^\./) 135 | .capture('text', not); 136 | }; 137 | 138 | /** 139 | * Expose text regex string 140 | */ 141 | 142 | module.exports.TEXT_REGEX = TEXT_REGEX; 143 | 144 | /** 145 | * Extglob parsers 146 | */ 147 | 148 | module.exports = parsers; 149 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const regex = require('regex-not'); 4 | const Cache = require('fragment-cache'); 5 | 6 | /** 7 | * Utils 8 | */ 9 | 10 | const utils = module.exports; 11 | const cache = utils.cache = new Cache(); 12 | 13 | /** 14 | * Cast `val` to an array 15 | * @return {Array} 16 | */ 17 | 18 | utils.arrayify = function(val) { 19 | if (!Array.isArray(val)) { 20 | return [val]; 21 | } 22 | return val; 23 | }; 24 | 25 | /** 26 | * Memoize a generated regex or function 27 | */ 28 | 29 | utils.memoize = function(type, pattern, options, fn) { 30 | const key = utils.createKey(type + pattern, options); 31 | 32 | if (cache.has(type, key)) { 33 | return cache.get(type, key); 34 | } 35 | 36 | const val = fn(pattern, options); 37 | if (options && options.cache === false) { 38 | return val; 39 | } 40 | 41 | cache.set(type, key, val); 42 | return val; 43 | }; 44 | 45 | /** 46 | * Create the key to use for memoization. The key is generated 47 | * by iterating over the options and concatenating key-value pairs 48 | * to the pattern string. 49 | */ 50 | 51 | utils.createKey = function(pattern, options) { 52 | let key = pattern; 53 | if (typeof options === 'undefined') { 54 | return key; 55 | } 56 | for (const prop in options) { 57 | key += ';' + prop + '=' + String(options[prop]); 58 | } 59 | return key; 60 | }; 61 | 62 | /** 63 | * Create the regex to use for matching text 64 | */ 65 | 66 | utils.createRegex = function(str) { 67 | const opts = {contains: true, strictClose: false}; 68 | return regex(str, opts); 69 | }; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extglob", 3 | "description": "Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.", 4 | "version": "3.0.0", 5 | "homepage": "https://github.com/micromatch/extglob", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "contributors": [ 8 | "Brian Woodward (https://twitter.com/doowb)", 9 | "Devon Govett (http://badassjs.com)", 10 | "Isiah Meadows (https://www.isiahmeadows.com)", 11 | "Jon Schlinkert (http://twitter.com/jonschlinkert)", 12 | "Matt Bierner (http://mattbierner.com)", 13 | "Shinnosuke Watanabe (https://shinnn.github.io)", 14 | "Daniel Tschinder (https://github.com/danez)" 15 | ], 16 | "repository": "micromatch/extglob", 17 | "bugs": { 18 | "url": "https://github.com/micromatch/extglob/issues" 19 | }, 20 | "license": "MIT", 21 | "files": [ 22 | "index.js", 23 | "lib" 24 | ], 25 | "main": "index.js", 26 | "engines": { 27 | "node": ">=4.0.0" 28 | }, 29 | "scripts": { 30 | "test": "mocha" 31 | }, 32 | "dependencies": { 33 | "array-unique": "^0.3.2", 34 | "define-property": "^2.0.2", 35 | "expand-brackets": "^4.0.0", 36 | "fragment-cache": "^0.2.1", 37 | "regex-not": "^1.0.0", 38 | "snapdragon": "^0.12.0", 39 | "snapdragon-capture": "^0.2.0", 40 | "to-regex": "^3.0.1" 41 | }, 42 | "devDependencies": { 43 | "bash-match": "^1.0.2", 44 | "for-own": "^1.0.0", 45 | "gulp": "^3.9.1", 46 | "gulp-eslint": "^4.0.0", 47 | "gulp-format-md": "^1.0.0", 48 | "gulp-istanbul": "^1.1.2", 49 | "gulp-mocha": "^3.0.1", 50 | "gulp-unused": "^0.2.1", 51 | "helper-changelog": "^0.3.0", 52 | "is-windows": "^1.0.1", 53 | "micromatch": "^3.0.4", 54 | "minimatch": "^3.0.4", 55 | "minimist": "^1.2.0", 56 | "mocha": "^3.5.0", 57 | "multimatch": "^2.1.0" 58 | }, 59 | "keywords": [ 60 | "bash", 61 | "extended", 62 | "extglob", 63 | "glob", 64 | "globbing", 65 | "ksh", 66 | "match", 67 | "pattern", 68 | "patterns", 69 | "regex", 70 | "test", 71 | "wildcard" 72 | ], 73 | "lintDeps": { 74 | "devDependencies": { 75 | "files": { 76 | "options": { 77 | "ignore": [ 78 | "benchmark/**/*.js" 79 | ] 80 | } 81 | } 82 | } 83 | }, 84 | "verb": { 85 | "toc": false, 86 | "layout": "default", 87 | "tasks": [ 88 | "readme" 89 | ], 90 | "related": { 91 | "list": [ 92 | "braces", 93 | "expand-brackets", 94 | "expand-range", 95 | "fill-range", 96 | "micromatch" 97 | ] 98 | }, 99 | "helpers": [ 100 | "helper-changelog" 101 | ], 102 | "plugins": [ 103 | "gulp-format-md" 104 | ], 105 | "lint": { 106 | "reflinks": true 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /test/_negations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * minimatch "tricky negations test" 5 | */ 6 | 7 | module.exports = { 8 | 'bar.min.js': { 9 | '*.!(js|css)': true, 10 | '!*.+(js|css)': false, 11 | '*.+(js|css)': true 12 | }, 13 | 14 | 'a-integration-test.js': { 15 | '*.!(j)': true, 16 | '*.!(js)': false, 17 | '!(*-integration-test.js)': false, 18 | '*-!(integration-)test.js': true, 19 | '*-!(integration)-test.js': false, 20 | '*!(-integration)-test.js': true, 21 | '*!(-integration-)test.js': true, 22 | '*!(integration)-test.js': true, 23 | '*!(integration-test).js': true, 24 | '*-!(integration-test).js': true, 25 | '*-!(integration-test.js)': true, 26 | '*-!(integra)tion-test.js': false, 27 | '*-integr!(ation)-test.js': false, 28 | '*-integr!(ation-t)est.js': false, 29 | '*-i!(ntegration-)test.js': false, 30 | '*i!(ntegration-)test.js': true, 31 | '*te!(gration-te)st.js': true, 32 | '*-!(integration)?test.js': false, 33 | '*?!(integration)?test.js': true 34 | }, 35 | 36 | 'foo-integration-test.js': { 37 | '@(foo-integration-test.js)': true, 38 | '@(*-integration-test.js)': true, 39 | '@(*-integration-test).js': true, 40 | 'foo-integration-test.js': true, 41 | '!(*-integration-test.js)': false 42 | }, 43 | 44 | 'foo.jszzz.js': { 45 | '*.!(js).js': true 46 | }, 47 | 48 | 'asd.jss': { 49 | '*.!(js)': true 50 | }, 51 | 52 | 'asd.jss.xyz': { 53 | '*.!(js)*.!(xy)': false, 54 | '*.!(js)*.!(xy)*': false, 55 | '*.!(js).!(xy)': false 56 | }, 57 | 58 | 'asd.jss.xy': { 59 | '*.!(js)*.!(xy)': false, 60 | '*.!(js)*.!(xy)*': false, 61 | '*.!(js).!(xy)': false, 62 | '*.!(js).!(xy)*': false 63 | }, 64 | 65 | 'asd.js.xyz': { 66 | '*.!(js)*.!(xy)': false, 67 | '*.!(js)*.!(xy)*': false, 68 | '*.!(js)*.!(xyz)': false, 69 | '*.!(js)*.!(xyz)*': false, 70 | '*.!(js).!(xy)': false, 71 | '*.!(js).!(xy)*': false, 72 | '*.!(js).!(xyz)': false, 73 | '*.!(js).!(xyz)*': false 74 | }, 75 | 76 | 'asd.js.xy': { 77 | '*.!(js).!(xy)': false 78 | }, 79 | 80 | 'asd.sjs.zxy': { 81 | '*.!(js).!(xy)': true 82 | }, 83 | 84 | 'asd..xyz': { 85 | '*.!(js).!(xy)': true 86 | }, 87 | 88 | 'asd..xy': { 89 | '*.!(js).!(xy)': false, 90 | '*.!(js|x).!(xy)': false 91 | }, 92 | 93 | 'foo.js.js': { 94 | '*!(js)': true, 95 | '*!(.js)': true, 96 | '*!(.js.js)': true, 97 | '*!(.js.js)*': true, 98 | '*(.js.js)': false, 99 | '**(.js.js)': true, 100 | '*(!(.js.js))': true, 101 | '*.!(js)*.!(js)': false, 102 | '*.!(js)+': false, 103 | '!(*(.js.js))': true, 104 | '*.!(js)': true, 105 | '*.!(js)*': false, // Bash 4.3 disagrees! 106 | '*.!(js)*.js': false // Bash 4.3 disagrees! 107 | }, 108 | 109 | 'a/foo.js.js': { 110 | '*/**(.*)': true, 111 | '*/**(.*.*)': true, 112 | 'a/**(.*.*)': true, 113 | '*/**(.js.js)': true, 114 | 'a/f*(!(.js.js))': true, 115 | 'a/!(*(.*))': true, 116 | 'a/!(+(.*))': true, 117 | 'a/!(*(.*.*))': true, 118 | '*/!(*(.*.*))': true, 119 | 'a/!(*(.js.js))': true 120 | }, 121 | 122 | 'testjson.json': { 123 | '*(*.json|!(*.js))': true, 124 | '+(*.json|!(*.js))': true, 125 | '@(*.json|!(*.js))': true, 126 | '?(*.json|!(*.js))': true 127 | }, 128 | 129 | 'foojs.js': { 130 | '*(*.json|!(*.js))': false, // Bash 4.3 disagrees! 131 | '*(*.json|!(*.js))*': true, 132 | '+(*.json|!(*.js))': false, // Bash 4.3 disagrees! 133 | '@(*.json|!(*.js))': false, 134 | '?(*.json|!(*.js))': false 135 | }, 136 | 137 | 'other.bar': { 138 | '*(*.json|!(*.js))': true, 139 | '*(*.json|!(*.js))*': true, 140 | '!(*(*.json|!(*.js)))*': false, 141 | '+(*.json|!(*.js))': true, 142 | '@(*.json|!(*.js))': true, 143 | '?(*.json|!(*.js))': true 144 | } 145 | }; 146 | -------------------------------------------------------------------------------- /test/bash.extglob.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var match = require('./support/match'); 5 | var assert = require('assert'); 6 | 7 | // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob.tests 8 | describe('bash tests derived from the pd-ksh test suite:', function() { 9 | var startLine = 11; 10 | var tests = [ 11 | [ '/dev/udp/129.22.8.102/45', '/dev/@(tcp|udp)/*/*', true ], 12 | '', 13 | 'valid numbers', 14 | [ '0', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees 15 | [ '12', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees 16 | [ '12abc', '0|[1-9]*([0-9])', false ], 17 | [ '1', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees 18 | '', 19 | 'octal numbers', 20 | [ '07', '+([0-7])', true ], 21 | [ '0377', '+([0-7])', true ], 22 | [ '09', '+([0-7])', false ], 23 | '', 24 | 'stuff from korn\'s book', 25 | [ 'paragraph', 'para@(chute|graph)', true ], 26 | [ 'paramour', 'para@(chute|graph)', false ], 27 | [ 'para991', 'para?([345]|99)1', true ], 28 | [ 'para381', 'para?([345]|99)1', false ], 29 | [ 'paragraph', 'para*([0-9])', false ], 30 | [ 'para', 'para*([0-9])', true ], 31 | [ 'para13829383746592', 'para*([0-9])', true ], 32 | [ 'paragraph', 'para*([0-9])', false ], 33 | [ 'para', 'para+([0-9])', false ], 34 | [ 'para987346523', 'para+([0-9])', true ], 35 | [ 'paragraph', 'para!(*.[0-9])', true ], 36 | [ 'para.38', 'para!(*.[0-9])', true ], 37 | [ 'para.graph', 'para!(*.[0-9])', true ], 38 | [ 'para39', 'para!(*.[0-9])', true ], 39 | '', 40 | 'tests derived from those rosenblatt\'s korn shell book', 41 | [ '', '*(0|1|3|5|7|9)', true ], 42 | [ '""', '*(0|1|3|5|7|9)', false ], 43 | [ '137577991', '*(0|1|3|5|7|9)', true ], 44 | [ '2468', '*(0|1|3|5|7|9)', false ], 45 | [ 'file.c', '*.c?(c)', true ], 46 | [ 'file.C', '*.c?(c)', false ], 47 | [ 'file.cc', '*.c?(c)', true ], 48 | [ 'file.ccc', '*.c?(c)', false ], 49 | [ 'parse.y', '!(*.c|*.h|Makefile.in|config*|README)', true ], 50 | [ 'shell.c', '!(*.c|*.h|Makefile.in|config*|README)', false ], 51 | [ 'Makefile', '!(*.c|*.h|Makefile.in|config*|README)', true ], 52 | [ '"VMS.FILE;1"', '*\\;[1-9]*([0-9])', false ], 53 | [ '"VMS.FILE;0"', '*\\;[1-9]*([0-9])', false ], 54 | [ '"VMS.FILE;"', '*\\;[1-9]*([0-9])', false ], 55 | [ '"VMS.FILE;139"', '*\\;[1-9]*([0-9])', false ], 56 | [ '"VMS.FILE;139"', '*;[1-9]*([0-9])', false ], 57 | [ '"VMS.FILE;139"', '*;[1-9]*([0-9])*', true ], 58 | [ '"VMS.FILE;139"', '*;[1-9]**([0-9])*', true ], 59 | [ '"VMS.FILE;1N"', '*;[1-9]*([0-9])', false ], 60 | '', 61 | ['abcx', '!([*)*', true], 62 | ['abcx', '!(\\[*)*', true], 63 | ['abcz', '!([*)*', true], 64 | ['abcz', '!(\\[*)*', true], 65 | ['bbc', '!([*)*', true], 66 | ['bbc', '!(\\[*)*', true], 67 | ['abcx', '+(a|b[)*', true], 68 | ['abcx', '+(a|b\\[)*', true], 69 | ['abcz', '+(a|b[)*', true], 70 | ['abcz', '+(a|b\\[)*', true], 71 | ['bbc', '+(a|b[)*', false], 72 | ['abcx', '[a*(]*z', false], 73 | ['abcx', '[a*\\(]*z', false], 74 | ['abcz', '[a*(]*z', true], 75 | ['abcz', '[a*\\(]*z', true], 76 | ['bbc', '[a*(]*z', false], 77 | ['bbc', '[a*\\(]*z', false], 78 | '', 79 | ['abc', '+()c', false], 80 | ['abc', '+()x', false], 81 | ['abc', '+(*)c', true], 82 | ['abc', '+(*)x', false], 83 | ['abc', 'no-file+(a|b)stuff', false], 84 | ['abc', 'no-file+(a*(c)|b)stuff', false], 85 | '', 86 | ['abc', 'a+(b|c)d', false], 87 | ['abd', 'a+(b|c)d', true], 88 | ['acd', 'a+(b|c)d', true], 89 | '', 90 | ['abc', 'a!(@(b|B))d', false], 91 | ['abd', 'a!(@(b|B))d', false], 92 | ['acd', 'a!(@(b|B))d', true], 93 | '', 94 | ['abc', 'a[b*(foo|bar)]d', false], 95 | ['abd', 'a[b*(foo|bar)]d', true], 96 | ['acd', 'a[b*(foo|bar)]d', false], 97 | '', 98 | 'simple kleene star tests', 99 | ['foo', '*(a|b[)', false], 100 | ['foo', '*(a|b[)|f*', true], 101 | 'this doesn\'t work right yet (from bash notes, it does work in extglob)', 102 | ['*(a|b[)', '*(a|b[)', true], 103 | '', 104 | 'More tests derived from a bug report concerning extended glob patterns following a *', 105 | ['ab', 'ab*(e|f)', true], 106 | ['abcdef', 'ab*(e|f)', false], 107 | ['abcfef', 'ab*(e|f)', false], 108 | ['abcfefg', 'ab*(e|f)', false], 109 | ['abef', 'ab*(e|f)', true], 110 | '', 111 | ['ab', 'b?*(e|f)', false], 112 | ['abcdef', 'b?*(e|f)', false], 113 | ['abcfef', 'b?*(e|f)', false], 114 | ['abcfefg', 'b?*(e|f)', false], 115 | ['abef', 'b?*(e|f)', false], 116 | '', 117 | ['ab', 'ab*d+(e|f)', false], 118 | ['abcdef', 'ab*d+(e|f)', true], 119 | ['abcfef', 'ab*d+(e|f)', false], 120 | ['abcfefg', 'ab*d+(e|f)', false], 121 | ['abef', 'ab*d+(e|f)', false], 122 | '', 123 | ['ab', 'ab**(e|f)', true], 124 | ['abcdef', 'ab**(e|f)', true], 125 | ['abcfef', 'ab**(e|f)', true], 126 | ['abcfefg', 'ab**(e|f)', true], 127 | ['abef', 'ab**(e|f)', true], 128 | '', 129 | ['ab', 'ab*+(e|f)', false], 130 | ['abcdef', 'ab*+(e|f)', true], 131 | ['abcfef', 'ab*+(e|f)', true], 132 | ['abcfefg', 'ab*+(e|f)', false], 133 | ['abef', 'ab*+(e|f)', true], 134 | '', 135 | ['ab', 'ab**(e|f)', true], 136 | ['abcdef', 'ab**(e|f)', true], 137 | ['abcfef', 'ab**(e|f)', true], 138 | ['abcfefg', 'ab**(e|f)', true], 139 | ['abef', 'ab**(e|f)', true], 140 | '', 141 | ['ab', 'ab**(e|f)g', false], 142 | ['abcdef', 'ab**(e|f)g', false], 143 | ['abcfef', 'ab**(e|f)g', false], 144 | ['abcfefg', 'ab**(e|f)g', true], 145 | ['abef', 'ab**(e|f)g', false], 146 | '', 147 | ['ab', 'ab*+(e|f)', false], 148 | ['abcdef', 'ab*+(e|f)', true], 149 | ['abcfef', 'ab*+(e|f)', true], 150 | ['abcfefg', 'ab*+(e|f)', false], 151 | ['abef', 'ab*+(e|f)', true], 152 | '', 153 | ['ab', 'ab***ef', false], 154 | ['abcdef', 'ab***ef', true], 155 | ['abcfef', 'ab***ef', true], 156 | ['abcfefg', 'ab***ef', false], 157 | ['abef', 'ab***ef', true], 158 | '', 159 | ['ab', 'ab**', true], 160 | ['abcdef', 'ab**', true], 161 | ['abcfef', 'ab**', true], 162 | ['abcfefg', 'ab**', true], 163 | ['abef', 'ab**', true], 164 | '', 165 | 'bug in all versions up to and including bash-2.05b', 166 | ['123abc', '*?(a)bc', true], 167 | '', 168 | 'character classes', 169 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a[^[:alnum:]]b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 170 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a[-.,:; _]b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 171 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@([^[:alnum:]])b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 172 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@([-.,:; _])b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 173 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@([.])b', ['a.b']], 174 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@([^.])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 175 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@([^x])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 176 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a+([^[:alnum:]])b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 177 | [['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'], 'a@(.|[^[:alnum:]])b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']], 178 | '', 179 | ['a , b', '*([[:space:]]),*([[:space:]])', false], 180 | ['a , b', 'a*([[:space:]]),*([[:space:]])b', true] 181 | ]; 182 | 183 | tests.forEach(function(test, i) { 184 | if (!Array.isArray(test) || !test) return; 185 | var fixture = test[0]; 186 | var pattern = test[1]; 187 | // if (pattern !== 'a[-.,:\;\ _]b') return; 188 | var expected = test[2]; 189 | var msg = ') should ' + (expected ? '' : 'not ') + 'match ' + pattern; 190 | 191 | it((startLine + i) + msg, function() { 192 | if (Array.isArray(fixture)) { 193 | match(fixture, pattern, expected, msg); 194 | } else { 195 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 196 | } 197 | }); 198 | }); 199 | }); 200 | -------------------------------------------------------------------------------- /test/bash.extglob1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var match = require('./support/match'); 5 | var assert = require('assert'); 6 | 7 | // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob1.sub 8 | describe('bash extglob1 tests', function() { 9 | var startLine = 11; 10 | var tests = [ 11 | ['a.c', '+([[:alpha:].])', true], 12 | ['a.c', '+([[:alpha:].])+([[:alpha:].])', true], 13 | ['a.c', '*([[:alpha:].])', true], 14 | ['a.c', '*([[:alpha:].])*([[:alpha:].])', true], 15 | ['a.c', '?([[:alpha:].])?([[:alpha:].])?([[:alpha:].])', true], 16 | ['a.c', '@([[:alpha:].])@([[:alpha:].])@([[:alpha:].])', true], 17 | ['.', '!([[:alpha:].])', false], 18 | ['.', '?([[:alpha:].])', true], 19 | ['.', '@([[:alpha:].])', true], 20 | ['.', '*([[:alpha:].])', true], 21 | ['.', '+([[:alpha:].])', true] 22 | ]; 23 | 24 | tests.forEach(function(test, i) { 25 | if (!Array.isArray(test)) return; 26 | var fixture = test[0]; 27 | var pattern = test[1]; 28 | var expected = test[2]; 29 | var msg = '"' + fixture + '" should ' + (expected ? '' : 'not ') + 'match ' + pattern; 30 | 31 | it((startLine + i) + ' ' + msg, function() { 32 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/bash.extglob1a.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var match = require('./support/match'); 5 | var assert = require('assert'); 6 | 7 | // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob1a.sub 8 | describe('bash extglob1a tests', function() { 9 | var startLine = 11; 10 | var tests = [ 11 | ['a', 'a*!(x)', true], 12 | ['ab', 'a*!(x)', true], 13 | ['ba', 'a*!(x)', false], 14 | ['ax', 'a*!(x)', true], 15 | ['a', 'a!(x)', true], 16 | ['ab', 'a!(x)', true], 17 | ['ba', 'a!(x)', false], 18 | ['ax', 'a!(x)', false], 19 | ['a', 'a*?(x)', true], 20 | ['ab', 'a*?(x)', true], 21 | ['ba', 'a*?(x)', false], 22 | ['ax', 'a*?(x)', true], 23 | ['a', 'a?(x)', true], 24 | ['ab', 'a?(x)', false], 25 | ['ba', 'a?(x)', false], 26 | ['ax', 'a?(x)', true] 27 | ]; 28 | 29 | tests.forEach(function(test, i) { 30 | if (!Array.isArray(test)) return; 31 | var fixture = test[0]; 32 | var pattern = test[1]; 33 | var expected = test[2]; 34 | var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern; 35 | 36 | it((startLine + i) + ' ' + msg, function() { 37 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/bash.extglob2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var assert = require('assert'); 5 | var match = require('./support/match'); 6 | 7 | // tests ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob2.tests 8 | describe('bash extglob2 tests', function() { 9 | var startLine = 11; 10 | var tests = [ 11 | [ 'fofo', '*(f*(o))', true ], 12 | [ 'ffo', '*(f*(o))', true ], 13 | [ 'foooofo', '*(f*(o))', true ], 14 | [ 'foooofof', '*(f*(o))', true ], 15 | [ 'fooofoofofooo', '*(f*(o))', true ], 16 | [ 'foooofof', '*(f+(o))', false ], 17 | [ 'xfoooofof', '*(f*(o))', false ], 18 | [ 'foooofofx', '*(f*(o))', false ], 19 | [ 'ofxoofxo', '*(*(of*(o)x)o)', true ], 20 | [ 'ofooofoofofooo', '*(f*(o))', false ], 21 | [ 'foooxfooxfoxfooox', '*(f*(o)x)', true ], 22 | [ 'foooxfooxofoxfooox', '*(f*(o)x)', false ], 23 | [ 'foooxfooxfxfooox', '*(f*(o)x)', true ], 24 | [ 'ofxoofxo', '*(*(of*(o)x)o)', true ], 25 | [ 'ofoooxoofxo', '*(*(of*(o)x)o)', true ], 26 | [ 'ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)', true ], 27 | [ 'ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)', true ], 28 | [ 'ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)', false ], 29 | [ 'ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)', true ], 30 | [ 'aac', '*(@(a))a@(c)', true ], 31 | [ 'ac', '*(@(a))a@(c)', true ], 32 | [ 'c', '*(@(a))a@(c)', false ], 33 | [ 'aaac', '*(@(a))a@(c)', true ], 34 | [ 'baaac', '*(@(a))a@(c)', false ], 35 | [ 'abcd', '?@(a|b)*@(c)d', true ], 36 | [ 'abcd', '@(ab|a*@(b))*(c)d', true ], 37 | [ 'acd', '@(ab|a*(b))*(c)d', true ], 38 | [ 'abbcd', '@(ab|a*(b))*(c)d', true ], 39 | [ 'effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ], 40 | [ 'efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ], 41 | [ 'egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ], 42 | [ 'egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ], 43 | [ 'egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', false ], 44 | [ 'ofoofo', '*(of+(o))', true ], 45 | [ 'oxfoxoxfox', '*(oxf+(ox))', true ], 46 | [ 'oxfoxfox', '*(oxf+(ox))', false ], 47 | [ 'ofoofo', '*(of+(o)|f)', true ], 48 | 'the following is supposed to match only as fo+ofo+ofo', 49 | [ 'foofoofo', '@(foo|f|fo)*(f|of+(o))', true ], 50 | [ 'oofooofo', '*(of|oof+(o))', true ], 51 | [ 'fffooofoooooffoofffooofff', '*(*(f)*(o))', true ], 52 | 'the following tests backtracking in alternation matches', 53 | [ 'fofoofoofofoo', '*(fo|foo)', true ], 54 | 'exclusion', 55 | [ 'foo', '!(x)', true ], 56 | [ 'foo', '!(x)*', true ], 57 | [ 'foo', '!(foo)', false ], 58 | [ 'foo', '!(foo)*', false ], // Bash 4.3 disagrees! 59 | [ 'foobar', '!(foo)', true ], 60 | [ 'foobar', '!(foo)*', false ], // Bash 4.3 disagrees! 61 | [ 'moo.cow', '!(*.*).!(*.*)', false ], // Bash 4.3 disagrees! 62 | [ 'mad.moo.cow', '!(*.*).!(*.*)', false ], 63 | [ 'mucca.pazza', 'mu!(*(c))?.pa!(*(z))?', false ], 64 | [ 'fff', '!(f)', true ], 65 | [ 'fff', '*(!(f))', true ], 66 | [ 'fff', '+(!(f))', true ], 67 | [ 'ooo', '!(f)', true ], 68 | [ 'ooo', '*(!(f))', true ], 69 | [ 'ooo', '+(!(f))', true ], 70 | [ 'foo', '!(f)', true ], 71 | [ 'foo', '*(!(f))', true ], 72 | [ 'foo', '+(!(f))', true ], 73 | [ 'f', '!(f)', false ], 74 | [ 'f', '*(!(f))', false ], 75 | [ 'f', '+(!(f))', false ], 76 | [ 'foot', '@(!(z*)|*x)', true ], 77 | [ 'zoot', '@(!(z*)|*x)', false ], 78 | [ 'foox', '@(!(z*)|*x)', true ], 79 | [ 'zoox', '@(!(z*)|*x)', true ], 80 | [ 'foo', '*(!(foo))', false ], // Bash 4.3 disagrees! 81 | [ 'foob', '!(foo)b*', false ], 82 | [ 'foobb', '!(foo)b*', false ] // Bash 4.3 disagrees! 83 | ]; 84 | 85 | tests.forEach(function(test, n) { 86 | if (!Array.isArray(test)) return; 87 | var fixture = test[0]; 88 | var pattern = test[1]; 89 | var expected = test[2]; 90 | var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern; 91 | 92 | it((startLine + n) + ' ' + msg, function() { 93 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /test/bash.extglob3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var match = require('./support/match'); 5 | var assert = require('assert'); 6 | 7 | // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob3.tests 8 | describe('bash extglob3 tests', function() { 9 | var startLine = 11; 10 | var tests = [ 11 | [ 'ab/../', '@(ab|+([^/]))/..?(/)', true ], 12 | [ 'ab/../', '+([^/])/..?(/)', true ], 13 | [ 'ab/../', '@(ab|?b)/..?(/)', true ], 14 | [ 'ab/../', '+([^/])/../', true ], 15 | [ 'ab/../', '+([!/])/..?(/)', true ], 16 | [ 'ab/../', '@(ab|+([!/]))/..?(/)', true ], 17 | [ 'ab/../', '+([!/])/../', true ], 18 | [ 'ab/../', '+([!/])/..?(/)', true ], 19 | [ 'ab/../', '+([!/])/..@(/)', true ], 20 | [ 'ab/../', '+(ab)/..?(/)', true ], 21 | [ 'ab/../', '[!/][!/]/../', true ], 22 | [ 'ab/../', '@(ab|?b)/..?(/)', true ], 23 | [ 'ab/../', '[^/][^/]/../', true ], 24 | [ 'ab/../', '?b/..?(/)', true ], 25 | [ 'ab/../', '+(?b)/..?(/)', true ], 26 | [ 'ab/../', '+(?b|?b)/..?(/)', true ], 27 | [ 'ab/../', '@(?b|?b)/..?(/)', true ], 28 | [ 'ab/../', '@(a?|?b)/..?(/)', true ], 29 | [ 'ab/../', '?(ab)/..?(/)', true ], 30 | [ 'ab/../', '?(ab|??)/..?(/)', true ], 31 | [ 'ab/../', '@(??)/..?(/)', true ], 32 | [ 'ab/../', '@(??|a*)/..?(/)', true ], 33 | [ 'ab/../', '@(a*)/..?(/)', true ], 34 | [ 'ab/../', '+(??)/..?(/)', true ], 35 | [ 'ab/../', '+(??|a*)/..?(/)', true ], 36 | [ 'ab/../', '+(a*)/..?(/)', true ], 37 | [ 'x', '@(x)', true ] 38 | ]; 39 | 40 | tests.forEach(function(test, i) { 41 | if (!Array.isArray(test)) return; 42 | var fixture = test[0]; 43 | var pattern = test[1]; 44 | var expected = test[2]; 45 | var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern; 46 | 47 | it((startLine + i) + ' ' + msg, function() { 48 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/bash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var match = require('./support/match'); 5 | var assert = require('assert'); 6 | 7 | describe('running extglob against minimatch tests', function() { 8 | var tests = [ 9 | ['*(a|b[)', '*(a|b\\[)', false], 10 | ['123abc', 'ab*d+(e|f)', false], 11 | ['123abc', 'ab?*(e|f)', false], 12 | ['a', '!(a)', false], 13 | ['a', '(b)', false], 14 | ['a', '??', false], 15 | ['a', 'a??b', false], 16 | ['a', 'b?(a|b)', false], 17 | ['a.', '*.+(b|d)', false], 18 | ['a', '!(*.a|*.b|*.c)', true], 19 | ['a.a', '!(*.[a-b]*)', false], 20 | ['a.a', '!(*.a|*.b|*.c)', false], 21 | ['a.a', '!(*[a-b].[a-b]*)', false], 22 | ['a.a', '!*.(a|b)', false], 23 | ['a.a', '!*.(a|b)*', false], 24 | ['a.a', '*.!(a)', false], 25 | ['a.a', '*.+(b|d)', false], 26 | ['a.a.a', '!(*.[a-b]*)', false], 27 | ['a.a.a', '!(*[a-b].[a-b]*)', false], 28 | ['a.a.a', '!*.(a|b)', false], 29 | ['a.a.a', '!*.(a|b)*', false], 30 | ['a.a.a', '*.+(b|d)', false], 31 | ['a.abcd', '!(*.a|*.b|*.c)', false], 32 | ['a.abcd', '!(*.a|*.b|*.c)*', false], 33 | ['a.abcd', '*.!(a|b|c)', false], 34 | ['a.abcd', '*.!(a|b|c)*', false], 35 | ['a.b', '!(*.*)', false], 36 | ['a.b', '!(*.[a-b]*)', false], 37 | ['a.b', '!(*[a-b].[a-b]*)', false], 38 | ['a.b', '!*.(a|b)', false], 39 | ['a.b', '!*.(a|b)*', false], 40 | ['a.bb', '!(*.[a-b]*)', false], 41 | ['a.bb', '!(*[a-b].[a-b]*)', false], 42 | ['a.bb', '!*.(a|b)', false], 43 | ['a.bb', '!*.(a|b)*', false], 44 | ['a.ccc', '!*.(a|b)', false], 45 | ['a.ccc', '!*.(a|b)*', false], 46 | ['a.ccc', '*.+(b|d)', false], 47 | ['a.js', '!(*.js)', false], 48 | ['a.js', '*.!(js)', false], 49 | ['a.js.js', '!(*.js)', false], 50 | ['aa', '?', false], 51 | ['aa', '@(a)b', false], 52 | ['aa', 'a??b', false], 53 | ['aab', '?', false], 54 | ['aab', '??', false], 55 | ['aab', '@(c)b', false], 56 | ['ab', 'a!(@(b|B))', false], 57 | ['aB', 'a!(@(b|B))', false], 58 | ['ab', 'a(*b', false], 59 | ['ab', 'ab**(e|f)g', false], 60 | ['ab', 'ab*+(e|f)', false], 61 | ['ab', 'ab*d+(e|f)', false], 62 | ['ab', 'ab?*(e|f)', false], 63 | ['abcdef', '(a+|b)+', false], 64 | ['abcdef', 'ab**(e|f)g', false], 65 | ['abcdef', 'ab?*(e|f)', false], 66 | ['abcfef', '(a+|b)+', false], 67 | ['abcfef', 'ab**(e|f)g', false], 68 | ['abcfef', 'ab*d+(e|f)', false], 69 | ['abcfefg', '(a+|b)+', false], 70 | ['abcfefg', 'ab*d+(e|f)', false], 71 | ['abcfefg', 'ab?*(e|f)', false], 72 | ['abd', '(a+|b)+', false], 73 | ['abd', 'a!(@(b|B))d', false], 74 | ['abd', 'ab*d+(e|f)', false], 75 | ['abef', '(a+|b)+', false], 76 | ['abef', '*(a+|b)', false], 77 | ['abef', 'ab**(e|f)g', false], 78 | ['abef', 'ab*d+(e|f)', false], 79 | ['acd', '(a+|b)+', false], 80 | ['acd', 'ab*d+(e|f)', false], 81 | ['acd', 'ab?*(e|f)', false], 82 | ['ax', 'a?(b*)', false], 83 | ['b/a', '!(b/a)', false], 84 | ['baaac', '*(@(a))a@(c)', false], 85 | ['bb', 'a?(a|b)', false], 86 | ['c', '*(@(a))a@(c)', false], 87 | ['c.a', '!(*.[a-b]*)', false], 88 | ['c.a', '!*.(a|b)', false], 89 | ['c.a', '!*.(a|b)*', false], 90 | ['c.a', '*.!(a)', false], 91 | ['c.a', '*.+(b|d)', false], 92 | ['c.js', '!(*.js)', false], 93 | ['c.js', '*.!(js)', false], 94 | ['cow', '.!(*.*)', false], 95 | ['d.a.d', '!*.(a|b)', false], 96 | ['d.a.d', '!*.(a|b)*', false], 97 | ['egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', false], 98 | ['f', '!(f)', false], 99 | ['f', '*(!(f))', false], 100 | ['f', '+(!(f))', false], 101 | ['f.a', '!(*.a|*.b|*.c)', false], 102 | ['f.a', '*.!(a|b|c)', false], 103 | ['foo', '!(foo)', false], 104 | ['foo', '!(foo)*', false], // bash 4.3 disagrees 105 | ['foo', '!(foo)+', false], 106 | ['foo', '!(foo)b*', false], 107 | ['foo', '*(!(foo))', false], 108 | ['foo.js.js', '*.!(js)*', false], 109 | ['foo.js.js', '*.!(js)*.!(js)', false], 110 | ['foo.js.js', '*.!(js)+', false], 111 | ['foob', '!(foo)b*', false], 112 | ['foobar', '!(foo)*', false], // bash 4.3 disagrees 113 | ['foobar', '!(foo)b*', false], 114 | ['foobb', '!(foo)b*', false], 115 | ['foooofof', '*(f+(o))', false], 116 | ['foooofofx', '*(f*(o))', false], 117 | ['foooxfooxofoxfooox', '*(f*(o)x)', false], 118 | ['mad.moo.cow', '!(*.*).!(*.*)', false], 119 | ['mad.moo.cow', '.!(*.*)', false], 120 | ['Makefile.in', '!(*.c|*.h|Makefile.in|config*|README)', false], 121 | ['moo', '.!(*.*)', false], 122 | ['moo.cow', '!(*.*).!(*.*)', false], // bash 4.3 disagrees 123 | ['moo.cow', '.!(*.*)', false], 124 | ['mucca.pazza', 'mu!(*(c))?.pa!(*(z))?', false], 125 | ['ofooofoofofooo', '*(f*(o))', false], 126 | ['ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)', false], 127 | ['oxfoxfox', '*(oxf+(ox))', false], 128 | ['shell.c', '!(*.c|*.h|Makefile.in|config*|README)', false], 129 | ['xfoooofof', '*(f*(o))', false], 130 | ['zoot', '@(!(z*)|*x)', false], 131 | ['zz', '(a+|b)*', false], 132 | ['a', '(a)', true], 133 | ['a', '*(a)', true], 134 | ['a', '+(a)', true], 135 | ['a', '?', true], 136 | ['a', '?(a|b)', true], 137 | ['a', 'a?(a|b)', true], 138 | ['a', 'a?(x)', true], 139 | ['a((((b', 'a(*b', true], 140 | ['a((b', 'a(*b', true], 141 | ['a(b', 'a(*b', true], 142 | ['a.', '!(*.a|*.b|*.c)', true], 143 | ['a.', '*!(.a|.b|.c)', true], 144 | ['a.', '*.!(a)', true], 145 | ['a.', '*.!(a|b|c)', true], 146 | ['a.a', '(a|d).(a|b)*', true], 147 | ['a.a', '*!(.a|.b|.c)', true], 148 | ['a.a.a', '*.!(a)', true], 149 | ['a.abcd', '*!(*.a|*.b|*.c)*', true], 150 | ['a.abcd', '*!(.a|.b|.c)', true], 151 | ['a.b', '(a|d).(a|b)*', true], 152 | ['a.b', '*!(.a|.b|.c)', true], 153 | ['a.b', '*.!(a)', true], 154 | ['a.b', '*.+(b|d)', true], 155 | ['a.bb', '(a|d).(a|b)*', true], 156 | ['a.bb', '*.+(b|d)', true], 157 | ['a.c', '*!(.a|.b|.c)', true], 158 | ['a.c.d', '!(*.a|*.b|*.c)', true], 159 | ['a.c.d', '*!(.a|.b|.c)', true], 160 | ['a.c.d', '*.!(a|b|c)', true], 161 | ['a.ccc', '!(*.[a-b]*)', true], 162 | ['a.ccc', '!(*[a-b].[a-b]*)', true], 163 | ['a.js', '*!(.js)', true], 164 | ['a.js.js', '*!(.js)', true], 165 | ['a.js.js', '*.!(js)', true], 166 | ['a.md', '!(*.js)', true], 167 | ['a.md', '*!(.js)', true], 168 | ['a.md', '*.!(js)', true], 169 | ['aa', '!(a)', true], 170 | ['aaac', '*(@(a))a@(c)', true], 171 | ['aab', 'a??b', true], 172 | ['aac', '*(@(a))a@(c)', true], 173 | ['ab', '!(*.*)', true], 174 | ['ab', '(a+|b)+', true], 175 | ['ab', 'ab**(e|f)', true], 176 | ['ab]', 'a!(@(b|B))', true], 177 | ['abab', 'ab**(e|f)', true], 178 | ['abb', '!(*.*)', true], 179 | ['abbcd', '@(ab|a*(b))*(c)d', true], 180 | ['aBc', 'a!(@(b|B))', true], 181 | ['abcd', '?@(a|b)*@(c)d', true], 182 | ['abcd', '@(ab|a*@(b))*(c)d', true], 183 | ['abcdef', '(a+|b)*', true], 184 | ['abcdef', 'ab**(e|f)', true], 185 | ['abcdef', 'ab*+(e|f)', true], 186 | ['abcdef', 'ab*d+(e|f)', true], 187 | ['abcfef', '(a+|b)*', true], 188 | ['abcfef', 'ab**(e|f)', true], 189 | ['abcfef', 'ab*+(e|f)', true], 190 | ['abcfef', 'ab?*(e|f)', true], 191 | ['abcfefg', '(a+|b)*', true], 192 | ['abcfefg', 'ab**(e|f)', true], 193 | ['abd', '(a+|b)*', true], 194 | ['abd', 'a!(@(b|B))', true], 195 | ['abd', 'ab**(e|f)', true], 196 | ['abd', 'ab?*(e|f)', true], 197 | ['abef', '(a+|b)*', true], 198 | ['abef', 'ab**(e|f)', true], 199 | ['abef', 'ab*+(e|f)', true], 200 | ['abef', 'ab?*(e|f)', true], 201 | ['ac', '*(@(a))a@(c)', true], 202 | ['ac', 'a!(@(b|B))', true], 203 | ['acd', '(a+|b)*', true], 204 | ['acd', '@(ab|a*(b))*(c)d', true], 205 | ['acd', 'a!(@(b|B))', true], 206 | ['acd', 'a!(@(b|B))d', true], 207 | ['ax', '?(a*|b)', true], 208 | ['b', '(a+|b)*', true], 209 | ['b/b', '!(b/a)', true], 210 | ['b/c', '!(b/a)', true], 211 | ['ba', 'b?(a|b)', true], 212 | ['bar', '!(foo)*', true], 213 | ['bar', '!(foo)b*', true], 214 | ['baz', '!(foo)*', true], 215 | ['baz', '!(foo)b*', true], 216 | ['c.a', '!(*[a-b].[a-b]*)', true], 217 | ['c.c', '*!(.a|.b|.c)', true], 218 | ['c.ccc', '!(*.[a-b]*)', true], 219 | ['c.ccc', '!(*[a-b].[a-b]*)', true], 220 | ['c.js', '*!(.js)', true], 221 | ['d.a.d', '!(*.[a-b]*)', true], 222 | ['d.a.d', '!(*[a-b].[a-b]*)', true], 223 | ['d.a.d', '*.!(a)', true], 224 | ['d.a.d', '*.+(b|d)', true], 225 | ['d.d', '!(*.a|*.b|*.c)', true], 226 | ['d.d', '*!(.a|.b|.c)', true], 227 | ['d.d', '*.!(a|b|c)', true], 228 | ['d.js.d', '!(*.js)', true], 229 | ['d.js.d', '*!(.js)', true], 230 | ['d.js.d', '*.!(js)', true], 231 | ['e.e', '!(*.a|*.b|*.c)', true], 232 | ['e.e', '*!(.a|.b|.c)', true], 233 | ['e.e', '*.!(a|b|c)', true], 234 | ['effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true], 235 | ['efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true], 236 | ['egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true], 237 | ['egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', true], 238 | ['f.a', '*!(.a|.b|.c)', true], 239 | ['f.f', '!(*.a|*.b|*.c)', true], 240 | ['f.f', '*!(.a|.b|.c)', true], 241 | ['f.f', '*.!(a|b|c)', true], 242 | ['fff', '!(f)', true], 243 | ['fff', '*(!(f))', true], 244 | ['fff', '+(!(f))', true], 245 | ['fffooofoooooffoofffooofff', '*(*(f)*(o))', true], 246 | ['ffo', '*(f*(o))', true], 247 | ['fofo', '*(f*(o))', true], 248 | ['fofoofoofofoo', '*(fo|foo)', true], 249 | ['foo', '!(f)', true], 250 | ['foo', '!(x)', true], 251 | ['foo', '!(x)*', true], 252 | ['foo', '*(!(f))', true], 253 | ['foo', '+(!(f))', true], 254 | ['foo.js.js', '*.!(js)', true], 255 | ['foobar', '!(foo)', true], 256 | ['foofoofo', '@(foo|f|fo)*(f|of+(o))', true], 257 | ['fooofoofofooo', '*(f*(o))', true], 258 | ['foooofo', '*(f*(o))', true], 259 | ['foooofof', '*(f*(o))', true], 260 | ['foooxfooxfoxfooox', '*(f*(o)x)', true], 261 | ['foooxfooxfxfooox', '*(f*(o)x)', true], 262 | ['foot', '@(!(z*)|*x)', true], 263 | ['foox', '@(!(z*)|*x)', true], 264 | ['Makefile', '!(*.c|*.h|Makefile.in|config*|README)', true], 265 | ['ofoofo', '*(of+(o))', true], 266 | ['ofoofo', '*(of+(o)|f)', true], 267 | ['ofoooxoofxo', '*(*(of*(o)x)o)', true], 268 | ['ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)', true], 269 | ['ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)', true], 270 | ['ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)', true], 271 | ['ofxoofxo', '*(*(of*(o)x)o)', true], 272 | ['oofooofo', '*(of|oof+(o))', true], 273 | ['ooo', '!(f)', true], 274 | ['ooo', '*(!(f))', true], 275 | ['ooo', '+(!(f))', true], 276 | ['oxfoxoxfox', '*(oxf+(ox))', true], 277 | ['parse.y', '!(*.c|*.h|Makefile.in|config*|README)', true], 278 | ['zoox', '@(!(z*)|*x)', true] 279 | ]; 280 | 281 | tests.forEach(function(test) { 282 | var fixture = test[0]; 283 | var pattern = test[1]; 284 | var expected = test[2]; 285 | var msg = '"' + fixture + '" should ' + (expected ? '' : 'not ') + 'match ' + pattern; 286 | 287 | it(msg, function() { 288 | assert.equal(match.isMatch(fixture, pattern), expected, msg); 289 | }); 290 | }); 291 | }); 292 | -------------------------------------------------------------------------------- /test/capture.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var capture = require('../').capture; 4 | var assert = require('assert'); 5 | 6 | describe('.capture()', function() { 7 | it('should return null if no match', function() { 8 | assert.equal(capture('test/(a|b)', 'hi/123'), null); 9 | }); 10 | 11 | it('should capture paren groups', function() { 12 | assert.deepEqual(capture('test/(a|b)/x.js', 'test/a/x.js'), ['a']); 13 | assert.deepEqual(capture('test/(a|b)/x.js', 'test/b/x.js'), ['b']); 14 | }); 15 | 16 | it('should capture star groups', function() { 17 | assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/a/x.js'), ['']); 18 | assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/aa/x.js'), ['a']); 19 | assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/ab/x.js'), ['b']); 20 | assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/aba/x.js'), ['ba']); 21 | }); 22 | 23 | it('should capture plus groups', function() { 24 | assert.deepEqual(capture('test/+(a|b)/x.js', 'test/a/x.js'), ['a']); 25 | assert.deepEqual(capture('test/+(a|b)/x.js', 'test/b/x.js'), ['b']); 26 | assert.deepEqual(capture('test/+(a|b)/x.js', 'test/ab/x.js'), ['ab']); 27 | assert.deepEqual(capture('test/+(a|b)/x.js', 'test/aba/x.js'), ['aba']); 28 | }); 29 | 30 | it('should capture optional groups', function() { 31 | assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/a/x.js'), ['']); 32 | assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/ab/x.js'), ['b']); 33 | assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/aa/x.js'), ['a']); 34 | }); 35 | 36 | it('should capture @ groups', function() { 37 | assert.deepEqual(capture('test/@(a|b)/x.js', 'test/a/x.js'), ['a']); 38 | assert.deepEqual(capture('test/@(a|b)/x.js', 'test/b/x.js'), ['b']); 39 | }); 40 | 41 | it('should capture negated groups', function() { 42 | assert.deepEqual(capture('test/!(a|b)/x.js', 'test/x/x.js'), ['x']); 43 | assert.deepEqual(capture('test/!(a|b)/x.js', 'test/y/x.js'), ['y']); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/errors.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var extglob = require('..'); 5 | 6 | describe('errors', function() { 7 | it('should throw an error when extglob() receives an invalid pattern', function(cb) { 8 | try { 9 | assert(extglob()); 10 | cb(new Error('expected an error')); 11 | } catch (err) { 12 | assert.equal(err.message, 'expected pattern to be a string'); 13 | } 14 | cb(); 15 | }); 16 | 17 | it('should throw an error when extglob.isMatch() receives an invalid pattern', function(cb) { 18 | try { 19 | assert(extglob.isMatch()); 20 | cb(new Error('expected an error')); 21 | } catch (err) { 22 | assert.equal(err.message, 'expected pattern to be a string'); 23 | } 24 | cb(); 25 | }); 26 | 27 | it('should throw an error when extglob.makeRe() receives an invalid pattern', function(cb) { 28 | try { 29 | assert(extglob.makeRe()); 30 | cb(new Error('expected an error')); 31 | } catch (err) { 32 | assert.equal(err.message, 'expected pattern to be a string'); 33 | } 34 | cb(); 35 | }); 36 | 37 | it('should throw an error when extglob.create() receives an invalid pattern', function(cb) { 38 | try { 39 | assert(extglob.create()); 40 | cb(new Error('expected an error')); 41 | } catch (err) { 42 | assert.equal(err.message, 'expected pattern to be a string'); 43 | } 44 | cb(); 45 | }); 46 | 47 | it('should throw an error when extglob.isMatch() receives an invalid string', function(cb) { 48 | try { 49 | assert(extglob.isMatch(null, '*')); 50 | cb(new Error('expected an error')); 51 | } catch (err) { 52 | assert.equal(err.message, 'expected a string'); 53 | } 54 | cb(); 55 | }); 56 | 57 | it('should throw an error when extglob.match() receives an invalid pattern', function(cb) { 58 | try { 59 | assert(extglob.match()); 60 | cb(new Error('expected an error')); 61 | } catch (err) { 62 | assert.equal(err.message, 'expected pattern to be a string'); 63 | } 64 | cb(); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/fixtures/posix.txt: -------------------------------------------------------------------------------- 1 | # A test suite for the POSIX.2 (BRE) pattern matching code 2 | LC_ALL=C 3 | LANG=C 4 | 5 | # First, test POSIX.2 character classes 6 | 7 | case e in 8 | [[:xdigit:]]) echo ok 1;; 9 | esac 10 | 11 | case a in 12 | [[:alpha:]123]) echo ok 2;; 13 | esac 14 | 15 | case 1 in 16 | [[:alpha:]123]) echo ok 3;; 17 | esac 18 | 19 | case 9 in 20 | [![:alpha:]]) echo ok 4;; 21 | esac 22 | 23 | # invalid character class expressions are just characters to be matched 24 | case a in 25 | [:al:]) echo ok 5;; 26 | esac 27 | 28 | case a in 29 | [[:al:]) echo ok 6;; 30 | esac 31 | 32 | case '!' in 33 | [abc[:punct:][0-9]) echo ok 7;; 34 | esac 35 | 36 | # let's try to match the start of a valid sh identifier 37 | case 'PATH' in 38 | [_[:alpha:]]*) echo ok 8;; 39 | esac 40 | 41 | # let's try to match the first two characters of a valid sh identifier 42 | case PATH in 43 | [_[:alpha:]][_[:alnum:]]*) echo ok 9;; 44 | esac 45 | 46 | # is ^C a cntrl character? 47 | case $'\003' in 48 | [[:cntrl:]]) echo ok 10;; 49 | esac 50 | 51 | # how about A? 52 | case A in 53 | [[:cntrl:]]) echo oops -- cntrl ;; 54 | *) echo ok 11;; 55 | esac 56 | 57 | case 9 in 58 | [[:digit:]]) echo ok 12;; 59 | esac 60 | 61 | case X in 62 | [[:digit:]]) echo oops -- digit;; 63 | *) echo ok 13;; 64 | esac 65 | 66 | case $'\033' in 67 | [[:graph:]]) echo oops -- graph;; 68 | *) echo ok 14;; 69 | esac 70 | 71 | case $'\040' in 72 | [[:graph:]]) echo oops -- graph 2;; 73 | *) echo ok 15;; 74 | esac 75 | 76 | case ' ' in 77 | [[:graph:]]) echo oops -- graph 3;; 78 | *) echo ok 16;; 79 | esac 80 | 81 | case 'aB' in 82 | [[:lower:]][[:upper:]]) echo ok 17;; 83 | esac 84 | 85 | case $'\040' in 86 | [[:print:]]) echo ok 18;; 87 | *) echo oops -- print;; 88 | esac 89 | 90 | case PS3 in 91 | [_[:alpha:]][_[:alnum:]][_[:alnum:]]*) echo ok 19;; 92 | esac 93 | 94 | case a in 95 | [[:alpha:][:digit:]]) echo ok 20;; 96 | *) echo oops - skip brackpat ;; 97 | esac 98 | 99 | case a in 100 | [[:alpha:]\]) echo oops -- dangling backslash in brackpat ;; 101 | *) echo ok 21 ;; 102 | esac 103 | 104 | # what's a newline? is it a blank? a space? 105 | case $'\n' in 106 | [[:blank:]]) echo ok -- blank ;; 107 | [[:space:]]) echo ok -- space ;; 108 | *) echo oops newline ;; 109 | esac 110 | 111 | # OK, what's a tab? is it a blank? a space? 112 | case $'\t' in 113 | [[:blank:]]) echo ok -- blank ;; 114 | [[:space:]]) echo ok -- space ;; 115 | *) echo oops newline ;; 116 | esac 117 | 118 | # let's check out characters in the ASCII range 119 | case $'\377' in 120 | [[:ascii:]]) echo oops -- ascii\?;; 121 | esac 122 | 123 | case 9 in 124 | [1[:alpha:]123]) echo oops 1;; 125 | esac 126 | 127 | # however, an unterminated brace expression containing a valid char class 128 | # that matches had better fail 129 | case a in 130 | [[:alpha:]) echo oops 2;; 131 | esac 132 | 133 | case $'\b' in 134 | [[:graph:]]) echo oops 3;; 135 | esac 136 | 137 | case $'\b' in 138 | [[:print:]]) echo oops 4;; 139 | esac 140 | 141 | case $' ' in 142 | [[:punct:]]) echo oops 5;; 143 | esac 144 | 145 | # Next, test POSIX.2 collating symbols 146 | 147 | case 'a' in 148 | [[.a.]]) echo ok 1;; 149 | esac 150 | 151 | case '-' in 152 | [[.hyphen.]-9]) echo ok 2;; 153 | esac 154 | 155 | case 'p' in 156 | [[.a.]-[.z.]]) echo ok 3;; 157 | esac 158 | 159 | case '-' in 160 | [[.-.]]) echo ok 4;; 161 | esac 162 | 163 | case ' ' in 164 | [[.space.]]) echo ok 5;; 165 | esac 166 | 167 | case ' ' in 168 | [[.grave-accent.]]) echo oops - grave;; 169 | *) echo ok 6;; 170 | esac 171 | 172 | case '4' in 173 | [[.-.]-9]) echo ok 7;; 174 | esac 175 | 176 | # an invalid collating symbol cannot be the first part of a range 177 | case 'c' in 178 | [[.yyz.]-[.z.]]) echo oops - yyz;; 179 | *) echo ok 8;; 180 | esac 181 | 182 | case 'c' in 183 | [[.yyz.][.a.]-z]) echo ok 9;; 184 | esac 185 | 186 | # but when not part of a range is not an error 187 | case 'c' in 188 | [[.yyz.][.a.]-[.z.]]) echo ok 10 ;; 189 | esac 190 | 191 | case 'p' in 192 | [[.a.]-[.Z.]]) echo oops -- bad range ;; 193 | *) echo ok 11;; 194 | esac 195 | 196 | case p in 197 | [[.a.]-[.zz.]p]) echo ok 12;; 198 | *) echo oops -- bad range 2;; 199 | esac 200 | 201 | case p in 202 | [[.aa.]-[.z.]p]) echo ok 13;; 203 | *) echo oops -- bad range 3;; 204 | esac 205 | 206 | case c in 207 | [[.yyz.]cde]) echo ok 14;; 208 | esac 209 | 210 | case abc in 211 | [[.cb.]a-Za]*) echo ok 15;; 212 | esac 213 | 214 | case $'\t' in 215 | [[.space.][.tab.][.newline.]]) echo ok 16;; 216 | esac 217 | 218 | # and finally, test POSIX.2 equivalence classes 219 | 220 | case "abc" in 221 | [[:alpha:]][[=b=]][[:ascii:]]) echo ok 1;; 222 | esac 223 | 224 | case "abc" in 225 | [[:alpha:]][[=B=]][[:ascii:]]) echo oops -- =B=;; 226 | *) echo ok 2 ;; 227 | esac 228 | 229 | case a in 230 | [[=b=]) echo oops;; # an incomplete equiv class is just a string 231 | *) echo ok 3;; 232 | esac 233 | 234 | -------------------------------------------------------------------------------- /test/options.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var matcher = require('./support/match'); 5 | var extglob = require('..'); 6 | 7 | describe('options', function() { 8 | describe('options.nonull', function() { 9 | it('should return the pattern when no matches are found', function() { 10 | matcher.match(['ax'], 'a?(b*)', []); 11 | matcher.match(['ax'], 'a?(b*)', ['a?(b*)'], {nonull: true}); 12 | matcher.match(['az'], 'a?(b*)', ['a?(b*)'], {nonull: true}); 13 | matcher.match(['ag'], 'a?(b*)', ['a?(b*)'], {nonull: true}); 14 | }); 15 | }); 16 | 17 | describe('options.failglob', function() { 18 | it('should throw an error when no matches are found', function(cb) { 19 | try { 20 | extglob.match(['ax'], 'a?(b*)', {failglob: true}); 21 | return cb(new Error('expected an error')); 22 | } catch (err) { 23 | assert(/no matches/.test(err.message)); 24 | } 25 | cb(); 26 | }); 27 | }); 28 | 29 | describe('options.strict', function() { 30 | it('should throw an error when an opening brace is missing', function(cb) { 31 | assert(!extglob.isMatch('foo', 'a)')); 32 | try { 33 | assert(!extglob.isMatch('foo', 'a)', {strict: true})); 34 | return cb(new Error('expected an error')); 35 | } catch (err) { 36 | assert(/missing/.test(err.message)); 37 | } 38 | cb(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/reference.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | var assert = require('assert'); 5 | var forOwn = require('for-own'); 6 | var matcher = require('./support/matcher'); 7 | var negations = require('./_negations'); 8 | 9 | describe('running extglob against minimatch tests', function() { 10 | forOwn(negations, function(val, fixture) { 11 | if (fixture !== 'asd.jss.xyz') return; 12 | describe('"' + fixture + '"', function() { 13 | forOwn(val, function(expected, pattern) { 14 | var exp = expected === false ? ' not' : ''; 15 | 16 | it('should' + exp + ' match "' + pattern + '"', function() { 17 | var actual = matcher.isMatch(fixture, pattern); 18 | if (actual === null) return; 19 | assert.equal(actual, expected, fixture + ' => ' + pattern); 20 | }); 21 | }); 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/support/compare.js: -------------------------------------------------------------------------------- 1 | module.exports = function(a, b) { 2 | a = a.toLowerCase(); 3 | b = b.toLowerCase(); 4 | return a > b ? 1 : a < b ? -1 : 0; 5 | }; 6 | -------------------------------------------------------------------------------- /test/support/match.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var matcher = require('./matcher'); 5 | var compare = require('./compare'); 6 | var utils = require('./utils'); 7 | 8 | module.exports = function(fixtures, pattern, expected, options, msg) { 9 | if (!Array.isArray(expected)) { 10 | var tmp = expected; 11 | expected = options; 12 | options = tmp; 13 | } 14 | 15 | if (typeof options === 'string') { 16 | msg = options; 17 | options = {}; 18 | } 19 | 20 | msg = msg ? (pattern + ' ' + msg) : pattern; 21 | 22 | var actual = matcher.match(utils.arrayify(fixtures), pattern, options); 23 | expected.sort(compare); 24 | actual.sort(compare); 25 | 26 | assert.deepEqual(actual, expected, msg); 27 | }; 28 | 29 | module.exports.match = function(fixtures, pattern, expected, options, msg) { 30 | if (!Array.isArray(expected)) { 31 | var tmp = expected; 32 | expected = options; 33 | options = tmp; 34 | } 35 | 36 | if (typeof options === 'string') { 37 | msg = options; 38 | options = {}; 39 | } 40 | 41 | msg = msg ? (pattern + ' ' + msg) : pattern; 42 | 43 | var actual = matcher.match(utils.arrayify(fixtures), pattern, options); 44 | expected.sort(compare); 45 | actual.sort(compare); 46 | 47 | assert.deepEqual(actual, expected, msg); 48 | }; 49 | 50 | module.exports.isMatch = function(fixture, pattern, options) { 51 | return matcher.isMatch.apply(null, arguments); 52 | }; 53 | 54 | module.exports.contains = function(fixture, pattern, options) { 55 | return matcher.contains.apply(null, arguments); 56 | }; 57 | 58 | module.exports.makeRe = function(pattern, options) { 59 | return matcher.makeRe.apply(null, arguments); 60 | }; 61 | -------------------------------------------------------------------------------- /test/support/matcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var argv = require('minimist')(process.argv.slice(2)); 4 | var mm = require('multimatch'); 5 | var minimatch = require('minimatch'); 6 | var bash = require('./try-bash'); 7 | var utils = require('./utils'); 8 | var extglob = require('../..'); 9 | 10 | // use multimatch for the array/array scenario 11 | function mi() { 12 | return mm.apply(null, arguments); 13 | } 14 | 15 | // label for debugging 16 | mm.multimatch = true; 17 | mi.minimatch = true; 18 | extglob.extglob = true; 19 | bash.bash = true; 20 | 21 | /** 22 | * Decorate methods onto multimatch for parity with nanomatch 23 | */ 24 | 25 | mm.isMatch = function(files, patterns, options) { 26 | return mm(utils.arrayify(files), patterns, options).length > 0; 27 | }; 28 | 29 | mm.contains = function(files, patterns, options) { 30 | return mm.isMatch(files, patterns, options); 31 | }; 32 | 33 | mm.match = function(files, patterns, options) { 34 | return mm(utils.arrayify(files), patterns, options); 35 | }; 36 | 37 | mm.makeRe = function(pattern, options) { 38 | return mi.makeRe(pattern, options); 39 | }; 40 | 41 | /** 42 | * Decorate methods onto minimatch for parity with nanomatch 43 | */ 44 | 45 | mi.isMatch = function(file, pattern, options) { 46 | return minimatch(file, pattern, options); 47 | }; 48 | 49 | mi.contains = function(files, patterns, options) { 50 | return mi.isMatch(files, patterns, options); 51 | }; 52 | 53 | mi.match = function(files, pattern, options) { 54 | return minimatch.match(utils.arrayify(files), pattern, options); 55 | }; 56 | 57 | mi.makeRe = function(pattern, options) { 58 | return minimatch.makeRe(pattern, options); 59 | }; 60 | 61 | /** 62 | * Detect matcher based on argv, with nanomatch as default 63 | */ 64 | 65 | var matcher = argv.mm ? mm : (argv.mi ? mi : extglob); 66 | if (argv.bash) { 67 | matcher = bash; 68 | } 69 | 70 | /** 71 | * Expose matcher 72 | */ 73 | 74 | module.exports = matcher; 75 | module.exports.bash = bash; 76 | module.exports.extglob = extglob; 77 | module.exports.mm = mm; 78 | module.exports.mi = mi; 79 | -------------------------------------------------------------------------------- /test/support/parse.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var mm = require('micromatch'); 6 | 7 | function parseFiles(pattern, options) { 8 | var opts = Object.assign({cwd: path.join(__dirname, '../fixtures')}, options); 9 | var cwd = opts.cwd; 10 | 11 | var files = mm(fs.readdirSync(cwd), pattern); 12 | var tests = {}; 13 | 14 | for (var i = 0; i < files.length; i++) { 15 | var file = files[i]; 16 | var name = path.basename(file, path.extname(file)); 17 | tests[name] = parse(path.join(cwd, file)); 18 | } 19 | return tests; 20 | } 21 | 22 | function parse(fp) { 23 | var str = fs.readFileSync(fp, 'utf8'); 24 | var lines = str.split('\n'); 25 | var len = lines.length; 26 | var idx = -1; 27 | var tests = []; 28 | 29 | while (++idx < len) { 30 | var line = lines[idx].trim(); 31 | 32 | if (!line) continue; 33 | if (/^#\s\w/.test(line)) { 34 | tests.push(line.replace(/^[#\s]+/, '').toLowerCase()); 35 | continue; 36 | } 37 | if (!/^[tf] /.test(line)) continue; 38 | 39 | var segs = line.split(/\s+/).filter(Boolean); 40 | if (segs.length !== 3) continue; 41 | tests.push([segs[1], segs[2], segs[0] === 't']); 42 | } 43 | return tests.filter(Boolean); 44 | } 45 | 46 | module.exports = parseFiles; 47 | -------------------------------------------------------------------------------- /test/support/try-bash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bash = require('bash-match'); 4 | var isWindows = require('is-windows'); 5 | 6 | function matcher() { 7 | if (isWindows()) return; 8 | return bash.apply(null, arguments); 9 | } 10 | 11 | matcher.match = function(fixtures, pattern) { 12 | if (isWindows()) return; 13 | try { 14 | return bash.match(fixtures, pattern); 15 | } catch (err) {} 16 | return null; 17 | }; 18 | 19 | matcher.isMatch = function(fixtures, pattern) { 20 | if (isWindows()) return; 21 | try { 22 | return bash.isMatch(fixtures, pattern); 23 | } catch (err) {} 24 | return null; 25 | }; 26 | 27 | /** 28 | * Expose `matcher` 29 | */ 30 | 31 | module.exports = matcher; 32 | -------------------------------------------------------------------------------- /test/support/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.arrayify = function(val) { 4 | return val ? (Array.isArray(val) ? val : [val]) : []; 5 | }; 6 | 7 | exports.alphaSort = function(a, b) { 8 | a = a.toLowerCase(); 9 | b = b.toLowerCase(); 10 | return a > b ? 1 : a < b ? -1 : 0; 11 | }; 12 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var match = require('./support/match'); 5 | var extglob = require('..'); 6 | 7 | /** 8 | * Most of these tests were converted directly from 9 | * bash 4.3 and 4.4 unit tests. 10 | */ 11 | 12 | describe('extglobs', function() { 13 | it('should export a function', function() { 14 | assert.equal(typeof extglob, 'function'); 15 | }); 16 | 17 | it.skip('failing unit tests from bash', function() { 18 | match(['moo.cow'], '!(*.*).!(*.*)', ['moo.cow']); 19 | match(['foo.js.js'], '*.!(js)*', ['foo.js.js']); 20 | }); 21 | 22 | it('should throw on imbalanced sets when `options.strictErrors` is true', function() { 23 | assert.throws(function() { 24 | match.isMatch('a((b', 'a(b', {strictErrors: true}); 25 | }, 'row:1 col:2 missing opening parens: "a(b"'); 26 | 27 | assert.throws(function() { 28 | match.isMatch('a((b', 'a(*b', {strictErrors: true}); 29 | }, 'row:1 col:2 missing opening parens: "a(*b"'); 30 | }); 31 | 32 | // from minimatch tests 33 | it('should match extglobs ending with statechar', function() { 34 | assert(!match.isMatch('ax', 'a?(b*)')); 35 | assert(match.isMatch('ax', '?(a*|b)')); 36 | }); 37 | 38 | it('should not choke on non-extglobs', function() { 39 | match(['c/z/v'], 'c/z/v', ['c/z/v']); 40 | }); 41 | 42 | it('should work with file extensions', function() { 43 | match(['.md', 'a.js', 'a.md', 'b.md', 'c.md'], '@(a|b).md', ['a.md', 'b.md']); 44 | match(['.md', 'a.js', 'a.md', 'aa.md', 'ab.md', 'b.md', 'bb.md', 'c.md'], '+(a|b).md', ['a.md', 'aa.md', 'ab.md', 'b.md', 'bb.md']); 45 | match(['.md', 'a.js', 'a.md', 'aa.md', 'ab.md', 'b.md', 'bb.md', 'c.md'], '*(a|b).md', ['.md', 'a.md', 'aa.md', 'ab.md', 'b.md', 'bb.md']); 46 | }); 47 | 48 | it('should support star (`*`) extglobs', function() { 49 | match(['cz', 'abz', 'az'], 'a*(z)', ['az']); 50 | match(['cz', 'abz', 'az'], 'a**(z)', ['az', 'abz']); 51 | match(['c/z/v', 'z', 'zf', 'fz'], '*(z)', ['z']); 52 | match(['c/z/v', 'c/a/v'], 'c/*(z)/v', ['c/z/v']); 53 | match(['a.js.js', 'a.md.js'], '*.*(js).js', ['a.js.js']); 54 | }); 55 | 56 | it('should support negation (`!`) extglobs', function() { 57 | match(['moo.cow'], '!(!(moo)).!(!(cow))', ['moo.cow']); 58 | match(['c/z/v', 'c/a/v'], 'c/!(z)/v', ['c/a/v']); 59 | match(['c/z/v', 'c/a/v'], 'c/!(z)/v', ['c/a/v']); 60 | match(['cz', 'abz', 'az'], 'a!(z)', ['abz']); 61 | match(['cz', 'abz', 'az'], 'a*!(z)', ['az', 'abz']); 62 | match(['c/a/v'], 'c/!(z)/v', ['c/a/v']); 63 | match(['a/z', 'a/b'], 'a/!(z)', ['a/b']); 64 | 65 | var f1 = ['a/a', 'a/b', 'a/c', 'b/a', 'b/b', 'b/c']; 66 | match(f1, '*(b/a)', ['b/a']); 67 | match(f1, '!(b/a)', [], {star: '[^/]*?'}); 68 | match(f1, '!((b/a))', [], {star: '[^/]*?'}); 69 | match(f1, '!((?:b/a))', [], {star: '[^/]*?'}); 70 | match(f1, '!(b/(a))', [], {star: '[^/]*?'}); 71 | 72 | match(f1, '!(b/a)', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']); 73 | match(f1, '!((b/a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']); 74 | match(f1, '!((?:b/a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']); 75 | match(f1, '!(b/(a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']); 76 | 77 | match(['b', 'b ', 'b ', 'c '], '@(!(a) {1,2})*', ['b ', 'b ', 'c ']); 78 | match(['b', 'b ', 'bb', 'ccc'], '@(!(a) {1,2})*', ['b ']); 79 | match(['b', 'a ', 'b ', 'bb', 'ccc'], '@(!(a){1,2} )*', ['b ']); 80 | 81 | var f2 = ['a', 'b', 'aa', 'ab', 'bb', 'ac', 'aaa', 'aab', 'abb', 'ccc']; 82 | match(f2, '!(a)', ['aa', 'aaa', 'aab', 'ab', 'abb', 'ac', 'b', 'bb', 'ccc']); 83 | match(f2, '!(a*)', ['b', 'bb', 'ccc']); 84 | match(f2, '!(*a*)', ['b', 'bb', 'ccc']); 85 | match(f2, '!(*a)', ['aab', 'ab', 'abb', 'ac', 'b', 'bb', 'ccc']); 86 | match(f2, '!(a)*', ['b', 'bb', 'ccc']); 87 | match(f2, '!(*a)*', ['b', 'bb', 'ccc']); 88 | match(f2, 'a!(b)*', ['a', 'aa', 'aaa', 'aab', 'ac']); 89 | match(['aajs', 'bajs', 'aamd', 'abmd'], 'a!(a)md', ['abmd']); 90 | match(['aajs', 'bajs', 'aamd', 'abmd'], 'a!(.)md', ['aamd', 'abmd']); 91 | match(['a.js', 'a.md', 'b.md', 'c.md'], '!(a|b).md', ['c.md']); 92 | match(['a.js', 'a.md', 'b.md', 'c.md'], '!(a|c).md', ['b.md']); 93 | match(['a.js', 'a.md', 'b.md', 'b.js', 'c.md'], '!(a|c).@(md|js)', ['b.md', 'b.js']); 94 | }); 95 | 96 | it('should support plus (`+`) extglobs', function() { 97 | match(['cz', 'abz', 'az'], 'a+(z)', ['az']); 98 | match(['c/z/v', 'z', 'zf', 'fz'], '+(z)', ['z']); 99 | match(['c/z/v', 'c/a/v'], 'c/+(z)/v', ['c/z/v']); 100 | match(['az', 'bz', 'axz'], 'a+(z)', ['az']); 101 | }); 102 | 103 | it('should support qmark (`?`) extglobs', function() { 104 | match(['c/z/v', 'z', 'zf', 'fz'], '?(z)', ['z']); 105 | match(['cz', 'abz', 'az'], 'a?(z)', ['az']); 106 | }); 107 | 108 | it('should support ampersand (`@`) extglobs', function() { 109 | match(['c/z/v', 'c/a/v'], 'c/@(z)/v', ['c/z/v']); 110 | match(['cz', 'abz', 'az'], 'a*@(z)', ['az', 'abz']); 111 | match(['cz', 'abz', 'az'], 'a@(z)', ['az']); 112 | }); 113 | 114 | it('should support qmark matching', function() { 115 | var arr = ['a', 'aa', 'ab', 'aaa', 'abcdefg']; 116 | match(arr, '?', ['a']); 117 | match(arr, '??', ['aa', 'ab']); 118 | match(arr, '???', ['aaa']); 119 | }); 120 | 121 | it('should match exactly one of the given pattern:', function() { 122 | var arr = ['aa.aa', 'a.bb', 'a.aa.a', 'cc.a', 'a.a', 'c.a', 'dd.aa.d', 'b.a']; 123 | match(arr, '(b|a).(a)', ['a.a', 'b.a']); 124 | match(arr, '@(b|a).@(a)', ['a.a', 'b.a']); 125 | }); 126 | 127 | it('stuff from korn\'s book', function() { 128 | assert(!match.isMatch('para', 'para+([0-9])')); 129 | assert(!match.isMatch('para381', 'para?([345]|99)1')); 130 | assert(!match.isMatch('paragraph', 'para*([0-9])')); 131 | assert(!match.isMatch('paragraph', 'para*([0-9])')); 132 | assert(!match.isMatch('paramour', 'para@(chute|graph)')); 133 | assert(match.isMatch('para', 'para*([0-9])')); 134 | assert(match.isMatch('para.38', 'para!(*.[00-09])')); 135 | assert(match.isMatch('para.graph', 'para!(*.[0-9])')); 136 | assert(match.isMatch('para13829383746592', 'para*([0-9])')); 137 | assert(match.isMatch('para39', 'para!(*.[0-9])')); 138 | assert(match.isMatch('para987346523', 'para+([0-9])')); 139 | assert(match.isMatch('para991', 'para?([345]|99)1')); 140 | assert(match.isMatch('paragraph', 'para!(*.[0-9])')); 141 | assert(match.isMatch('paragraph', 'para@(chute|graph)')); 142 | }); 143 | 144 | it('tests derived from those in rosenblatt\'s korn shell book', function() { 145 | match(['', '137577991', '2468'], '*(0|1|3|5|7|9)', ['', '137577991']); 146 | match(['file.c', 'file.C', 'file.cc', 'file.ccc'], '*.c?(c)', ['file.c', 'file.cc']); 147 | match(['parse.y', 'shell.c', 'Makefile', 'Makefile.in'], '!(*.c|*.h|Makefile.in|config*|README)', ['parse.y', 'Makefile']); 148 | match(['VMS.FILE;', 'VMS.FILE;0', 'VMS.FILE;1', 'VMS.FILE;139', 'VMS.FILE;1N'], '*\\;[1-9]*([0-9])', ['VMS.FILE;1', 'VMS.FILE;139']); 149 | }); 150 | 151 | it('tests derived from the pd-ksh test suite', function() { 152 | match(['abcx', 'abcz', 'bbc'], '!([[*])*', ['abcx', 'abcz', 'bbc']); 153 | match(['abcx', 'abcz', 'bbc'], '+(a|b\\[)*', ['abcx', 'abcz']); 154 | match(['abd', 'acd'], 'a+(b|c)d', ['abd', 'acd']); 155 | match(['abd', 'acd', 'ac', 'ab'], 'a!(@(b|B))', ['acd', 'abd', 'ac']); 156 | match(['abd', 'acd'], 'a!(@(b|B))d', ['acd']); 157 | match(['abd', 'acd'], 'a[b*(foo|bar)]d', ['abd']); 158 | match(['abcx', 'abcz', 'bbc', 'aaz', 'aaaz'], '[a*(]*z', ['aaz', 'aaaz', 'abcz']); 159 | }); 160 | 161 | it('simple kleene star tests', function() { 162 | assert(!match.isMatch('foo', '*(a|b\\[)')); 163 | assert(match.isMatch('foo', '*(a|b\\[)|f*')); 164 | }); 165 | 166 | it('this doesn\'t work in bash either (per bash extglob.tests notes)', function() { 167 | assert(!match.isMatch('*(a|b[)', '*(a|b\\[)')); 168 | assert(match.isMatch('*(a|b[)', '\\*\\(a\\|b\\[\\)')); 169 | }); 170 | 171 | it('should support multiple extglobs:', function() { 172 | var arr = ['a.a', 'a.b', 'a.c', 'a.c.d', 'c.c', 'a.', 'd.d', 'e.e', 'f.f', 'a.abcd']; 173 | match(arr, '*.(a|b|@(ab|a*@(b))*(c)d)', ['a.a', 'a.b', 'a.abcd']); 174 | match(arr, '!(*.a|*.b|*.c)', ['a.', 'a.c.d', 'd.d', 'e.e', 'f.f']); 175 | match(arr, '!(*.[^a-c])', ['a.a', 'a.b', 'a.c', 'c.c', 'a.', 'a.abcd']); 176 | match(arr, '!(*.[a-c])', ['a.', 'a.c.d', 'a.abcd', 'd.d', 'e.e', 'f.f']); 177 | match(arr, '!(*.[a-c]*)', ['a.', 'a.c.d', 'd.d', 'e.e', 'f.f']); 178 | match(arr, '!(*.[a-c])*', ['a.', 'd.d', 'e.e', 'f.f']); 179 | match(arr, '*!(.a|.b|.c)', arr); 180 | match(arr, '*!(.a|.b|.c)*', arr); 181 | match(arr, '*.!(a|b|c)', ['a.c.d', 'a.', 'd.d', 'e.e', 'f.f']); 182 | match(arr, '*.!(a|b|c)*', ['a.c.d', 'a.', 'd.d', 'e.e', 'f.f']); 183 | }); 184 | 185 | it('should correctly match empty parens', function() { 186 | match(['def', 'ef'], '()ef', ['ef']); 187 | }); 188 | 189 | it('should match escaped parens', function() { 190 | var arr = ['a(b', 'a\\(b', 'a((b', 'a((((b', 'ab']; 191 | match(arr, 'a(b', ['a(b']); 192 | match(arr, 'a\\(b', ['a(b']); 193 | match(arr, 'a(*b', ['a(b', 'a((b', 'a((((b']); 194 | }); 195 | 196 | it('should match escaped backslashes', function() { 197 | match(['a(b', 'a\\(b', 'a((b', 'a((((b', 'ab'], 'a\\\\(b', ['a\\(b']); 198 | match(['a\\b', 'a/b', 'ab'], 'a\\\\b', ['a\\b']); 199 | }); 200 | 201 | // these are not extglobs, and do not need to pass, but they are included 202 | // to test integration with expand-brackets 203 | it('should match common regex patterns', function() { 204 | var fixtures = ['a c', 'a1c', 'a123c', 'a.c', 'a.xy.zc', 'a.zc', 'abbbbc', 'abbbc', 'abbc', 'abc', 'abq', 'axy zc', 'axy', 'axy.zc', 'axyzc']; 205 | 206 | match(['a\\b', 'a/b', 'ab'], 'a/b', ['a/b']); 207 | match(fixtures, 'ab?bc', ['abbbc']); 208 | match(fixtures, 'ab*c', ['abbbbc', 'abbbc', 'abbc', 'abc']); 209 | match(fixtures, 'ab+bc', ['abbbbc', 'abbbc', 'abbc']); 210 | match(fixtures, '^abc$', ['abc']); 211 | match(fixtures, 'a.c', ['a.c']); 212 | match(fixtures, 'a.*c', ['a.c', 'a.xy.zc', 'a.zc']); 213 | match(fixtures, 'a*c', ['a c', 'a.c', 'a1c', 'a123c', 'abbbbc', 'abbbc', 'abbc', 'abc', 'axyzc', 'axy zc', 'axy.zc', 'a.xy.zc', 'a.zc']); 214 | match(fixtures, 'a\\w+c', ['a1c', 'a123c', 'abbbbc', 'abbbc', 'abbc', 'abc', 'axyzc'], 'Should match word characters'); 215 | match(fixtures, 'a\\W+c', ['a.c', 'a c'], 'Should match non-word characters'); 216 | match(fixtures, 'a\\d+c', ['a1c', 'a123c'], 'Should match numbers'); 217 | match(['foo@#$%123ASD #$$%^&', 'foo!@#$asdfl;', '123'], '\\d+', ['123']); 218 | match(['a123c', 'abbbc'], 'a\\D+c', ['abbbc'], 'Should match non-numbers'); 219 | match(['foo', ' foo '], '(f|o)+\\b', ['foo'], 'Should match word boundaries'); 220 | }); 221 | }); 222 | 223 | describe('bash unit tests', function() { 224 | var fixtures = ['ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foob', 'foobb', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof', 'foooofofx', 'foooxfooxfoxfooox', 'foooxfooxfxfooox', 'foooxfooxofoxfooox', 'foot', 'foox', 'ofoofo', 'ofooofoofofooo', 'ofoooxoofxo', 'ofoooxoofxoofoooxoofxo', 'ofoooxoofxoofoooxoofxofo', 'ofoooxoofxoofoooxoofxoo', 'ofoooxoofxoofoooxoofxooofxofxo', 'ofxoofxo', 'oofooofo', 'ooo', 'oxfoxfox', 'oxfoxoxfox', 'xfoooofof']; 225 | 226 | it('should match extended globs from the bash spec:', function() { 227 | var f2 = ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']; 228 | match(f2, '!(foo)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 229 | match(f2, '!(!(foo))', ['foo']); 230 | match(f2, '!(!(!(foo)))', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 231 | match(f2, '!(!(!(!(foo))))', ['foo']); 232 | match(f2, '!(!(foo))*', ['foo', 'foo/bar', 'foobar', 'foot', 'foox']); 233 | match(f2, '!(f!(o))', ['fo']); 234 | match(f2, '!(f(o))', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 235 | match(f2, '!(f)', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 236 | match(f2, '!(f)', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 237 | match(f2, '!(foo)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 238 | match(f2, '!(foo)*', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 239 | match(f2, '!(x)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'xx']); 240 | match(f2, '!(x)*', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox']); 241 | match(f2, '*(!(f))', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 242 | match(f2, '*((foo))', ['foo']); 243 | match(f2, '+(!(f))', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 244 | match(f2, '@(!(z*)|*x)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']); 245 | match(f2, 'foo/!(foo)', ['foo/bar']); 246 | 247 | match(fixtures, '(foo)bb', ['foobb']); 248 | match(fixtures, '*(*(f)*(o))', [ 'ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof', 'ofoofo', 'ofooofoofofooo', 'oofooofo', 'ooo']); 249 | match(fixtures, '*(*(of*(o)x)o)', [ 'ofoooxoofxo', 'ofoooxoofxoofoooxoofxo', 'ofoooxoofxoofoooxoofxoo', 'ofoooxoofxoofoooxoofxooofxofxo', 'ofxoofxo', 'ooo']); 250 | match(fixtures, '*(f*(o))', ['ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof']); 251 | match(fixtures, '*(f*(o)x)', ['foooxfooxfoxfooox', 'foooxfooxfxfooox', 'foox']); 252 | match(fixtures, '*(f+(o))', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo']); 253 | match(fixtures, '*(of+(o))', ['ofoofo']); 254 | match(fixtures, '*(of+(o)|f)', ['fofo', 'fofoofoofofoo', 'ofoofo', 'ofooofoofofooo']); 255 | match(fixtures, '*(of|oof+(o))', ['ofoofo', 'oofooofo']); 256 | match(fixtures, '*(oxf+(ox))', ['oxfoxoxfox']); 257 | match(fixtures, '@(!(z*)|*x)', ['ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foob', 'foobb', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof', 'foooofofx', 'foooxfooxfoxfooox', 'foooxfooxfxfooox', 'foooxfooxofoxfooox', 'foot', 'foox', 'ofoofo', 'ofooofoofofooo', 'ofoooxoofxo', 'ofoooxoofxoofoooxoofxo', 'ofoooxoofxoofoooxoofxofo', 'ofoooxoofxoofoooxoofxoo', 'ofoooxoofxoofoooxoofxooofxofxo', 'ofxoofxo', 'oofooofo', 'ooo', 'oxfoxfox', 'oxfoxoxfox', 'xfoooofof']); 258 | match(fixtures, '@(foo|f|fo)*(f|of+(o))', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo']); 259 | 260 | var arr = ['aaac', 'aac', 'ac', 'abbcd', 'abcd', 'acd', 'baaac', 'c', 'foo']; 261 | match(arr, '*(@(a))a@(c)', ['aaac', 'aac', 'ac']); 262 | match(arr, '@(ab|a*(b))*(c)d', ['abbcd', 'abcd', 'acd']); 263 | match(arr, '?@(a|b)*@(c)d', ['abbcd', 'abcd']); 264 | match(arr, '@(ab|a*@(b))*(c)d', ['abbcd', 'abcd']); 265 | match(['aac'], '*(@(a))b@(c)', []); 266 | }); 267 | 268 | it('should backtrack in alternation matches', function() { 269 | match(fixtures, '*(fo|foo)', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo']); 270 | }); 271 | 272 | it('should support exclusions', function() { 273 | match(['foob', 'foobb', 'foo', 'bar', 'baz', 'foobar'], '!(foo)b*', ['bar', 'baz']); 274 | match(['foo', 'bar', 'baz', 'foobar'], '*(!(foo))', ['bar', 'baz', 'foobar']); 275 | 276 | // Bash 4.3 says this should match `foo` and `foobar` too 277 | match(['foo', 'bar', 'baz', 'foobar'], '!(foo)*', ['bar', 'baz']); 278 | 279 | match(['moo.cow', 'moo', 'cow'], '!(*.*)', ['moo', 'cow']); 280 | match(['mad.moo.cow'], '!(*.*).!(*.*)', []); 281 | match(['moo.cow', 'moo', 'cow'], '!(*.*).', []); 282 | match(['moo.cow', 'moo', 'cow'], '.!(*.*)', []); 283 | match(['mucca.pazza'], 'mu!(*(c))?.pa!(*(z))?', []); 284 | 285 | match(['effgz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['effgz']); 286 | match(['efgz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['efgz']); 287 | match(['egz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['egz']); 288 | match(['egz'], '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', []); 289 | match(['egzefffgzbcdij'], '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['egzefffgzbcdij']); 290 | }); 291 | 292 | it('valid numbers', function() { 293 | assert(match.isMatch('/dev/udp/129.22.8.102/45', '/dev/@(tcp|udp)/*/*')); 294 | match(['0', '12', '1', '12abc', '555'], '[1-6]([0-9])', ['12']); 295 | match(['0', '12', '1', '12abc', '555'], '[1-6]*([0-9])', ['1', '12', '555']); 296 | match(['0', '12', '1', '12abc', '555'], '[1-5]*([6-9])', ['1']); 297 | match(['0', '12', '1', '12abc', '555'], '0|[1-6]*([0-9])', ['0', '1', '12', '555']); 298 | match(['07', '0377', '09'], '+([0-7])', ['0377', '07']); 299 | }); 300 | 301 | it('stuff from korn\'s book', function() { 302 | assert(!match.isMatch('para', 'para+([0-9])')); 303 | assert(!match.isMatch('para381', 'para?([345]|99)1')); 304 | assert(!match.isMatch('paragraph', 'para*([0-9])')); 305 | assert(!match.isMatch('paragraph', 'para*([0-9])')); 306 | assert(!match.isMatch('paramour', 'para@(chute|graph)')); 307 | assert(match.isMatch('para', 'para*([0-9])')); 308 | assert(match.isMatch('para.38', 'para!(*.[0-9])')); 309 | assert(match.isMatch('para.graph', 'para!(*.[0-9])')); 310 | assert(match.isMatch('para13829383746592', 'para*([0-9])')); 311 | assert(match.isMatch('para39', 'para!(*.[0-9])')); 312 | assert(match.isMatch('para987346523', 'para+([0-9])')); 313 | assert(match.isMatch('para991', 'para?([345]|99)1')); 314 | assert(match.isMatch('paragraph', 'para!(*.[0-9])')); 315 | assert(match.isMatch('paragraph', 'para@(chute|graph)')); 316 | }); 317 | 318 | it('tests derived from those in rosenblatt\'s korn shell book', function() { 319 | assert(match.isMatch('', '*(0|1|3|5|7|9)')); 320 | assert(match.isMatch('137577991', '*(0|1|3|5|7|9)')); 321 | assert(!match.isMatch('2468', '*(0|1|3|5|7|9)')); 322 | 323 | assert(!match.isMatch('file.C', '*.c?(c)')); 324 | assert(!match.isMatch('file.ccc', '*.c?(c)')); 325 | assert(match.isMatch('file.c', '*.c?(c)')); 326 | assert(match.isMatch('file.cc', '*.c?(c)')); 327 | 328 | assert(match.isMatch('parse.y', '!(*.c|*.h|Makefile.in|config*|README)')); 329 | assert(match.isMatch('Makefile', '!(*.c|*.h|Makefile.in|config*|README)')); 330 | assert(!match.isMatch('shell.c', '!(*.c|*.h|Makefile.in|config*|README)')); 331 | 332 | assert(!match.isMatch('VMS.FILE;', '*\\;[1-9]*([0-9])')); 333 | assert(!match.isMatch('VMS.FILE;0', '*\\;[1-9]*([0-9])')); 334 | assert(!match.isMatch('VMS.FILE;1N', '*\\;[1-9]*([0-9])')); 335 | assert(match.isMatch('VMS.FILE;1', '*\\;[1-9]*([0-9])')); 336 | assert(match.isMatch('VMS.FILE;139', '*\\;[1-9]*([0-9])')); 337 | }); 338 | 339 | it('tests derived from the pd-ksh test suite', function() { 340 | match(['abcx', 'abcz', 'bbc'], '!([*)*', ['abcx', 'abcz', 'bbc']); 341 | match(['abcx', 'abcz', 'bbc'], '+(a|b[)*', ['abcx', 'abcz']); 342 | match(['abcx', 'abcz', 'bbc'], '[a*(]*)z', []); 343 | 344 | match(['abc'], '+()c', []); 345 | match(['abc'], '+()x', []); 346 | match(['abc'], '+(*)c', ['abc']); 347 | match(['abc'], '+(*)x', []); 348 | 349 | match(['abc'], 'no-file+(a|b)stuff', []); 350 | match(['abc'], 'no-file+(a*(c)|b)stuff', []); 351 | 352 | match(['abd', 'acd'], 'a+(b|c)d', ['abd', 'acd']); 353 | match(['abc'], 'a+(b|c)d', []); 354 | 355 | match(['acd'], 'a!(@(b|B))d', ['acd']); 356 | match(['abc', 'abd'], 'a!(@(b|B))d', []); 357 | 358 | match(['abd'], 'a[b*(foo|bar)]d', ['abd']); 359 | match(['abc', 'acd'], 'a[b*(foo|bar)]d', []); 360 | }); 361 | 362 | it('simple kleene star tests', function() { 363 | assert(!match.isMatch('foo', '*(a|b[)')); 364 | assert(!match.isMatch('(', '*(a|b[)')); 365 | assert(!match.isMatch(')', '*(a|b[)')); 366 | assert(!match.isMatch('|', '*(a|b[)')); 367 | assert(match.isMatch('a', '*(a|b)')); 368 | assert(match.isMatch('b', '*(a|b)')); 369 | assert(match.isMatch('b[', '*(a|b\\[)')); 370 | assert(match.isMatch('ab[', '+(a|b\\[)')); 371 | assert(!match.isMatch('ab[cde', '+(a|b\\[)')); 372 | assert(match.isMatch('ab[cde', '+(a|b\\[)*')); 373 | }); 374 | 375 | it('check extended globbing in pattern removal', function() { 376 | match(['a', 'abc'], '+(a|abc)', ['a', 'abc']); 377 | match(['abcd', 'abcde', 'abcedf'], '+(a|abc)', []); 378 | 379 | match(['f'], '+(def|f)', ['f']); 380 | 381 | match(['def'], '+(f|def)', ['def']); 382 | match(['cdef', 'bcdef', 'abcedf'], '+(f|def)', []); 383 | 384 | match(['abcd'], '*(a|b)cd', ['abcd']); 385 | match(['a', 'ab', 'abc'], '*(a|b)cd', []); 386 | 387 | match(['a', 'ab', 'abc', 'abcde', 'abcdef'], '"*(a|b)cd"', []); 388 | }); 389 | 390 | it('More tests derived from a bug report (in bash) concerning extended glob patterns following a *', function() { 391 | var fixtures = ['123abc', 'ab', 'abab', 'abcdef', 'accdef', 'abcfefg', 'abef', 'abcfef', 'abd', 'acd']; 392 | match(['/dev/udp/129.22.8.102/45'], '/dev\\/@(tcp|udp)\\/*\\/*', ['/dev/udp/129.22.8.102/45']); 393 | match(fixtures, '(a+|b)*', ['ab', 'abab', 'accdef', 'abcdef', 'abcfefg', 'abef', 'abcfef', 'abd', 'acd']); 394 | match(fixtures, '(a+|b)+', ['ab', 'abab']); 395 | match(fixtures, 'a(b*(foo|bar))d', ['abd']); 396 | match(fixtures, 'ab*(e|f)', ['ab', 'abef']); 397 | match(fixtures, 'ab**(e|f)', ['ab', 'abab', 'abcdef', 'abcfef', 'abd', 'abef', 'abcfefg']); 398 | match(fixtures, 'ab**(e|f)g', ['abcfefg']); 399 | match(fixtures, 'ab***ef', ['abcdef', 'abcfef', 'abef']); 400 | match(fixtures, 'ab*+(e|f)', ['abcdef', 'abcfef', 'abef']); 401 | match(fixtures, 'ab*d*(e|f)', ['abcdef', 'abd']); 402 | match(fixtures, 'ab*d+(e|f)', ['abcdef']); 403 | match(fixtures, 'ab?*(e|f)', ['abcfef', 'abd', 'abef']); 404 | }); 405 | 406 | it('bug in all versions up to and including bash-2.05b', function() { 407 | assert(match.isMatch('123abc', '*?(a)bc')); 408 | }); 409 | 410 | it('should work with character classes', function() { 411 | var fixtures = ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']; 412 | match(fixtures, 'a[^[:alnum:]]b', fixtures); 413 | match(fixtures, 'a[-.,:\\;\\ _]b', fixtures); 414 | match(fixtures, 'a@([^[:alnum:]])b', fixtures); 415 | match(fixtures, 'a@([-.,:; _])b', fixtures); 416 | match(fixtures, 'a@([.])b', ['a.b']); 417 | match(fixtures, 'a@([^.])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']); 418 | match(fixtures, 'a@([^x])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']); 419 | match(fixtures, 'a+([^[:alnum:]])b', fixtures); 420 | match(fixtures, 'a@(.|[^[:alnum:]])b', fixtures); 421 | }); 422 | 423 | it('should support POSIX character classes in extglobs', function() { 424 | assert(match.isMatch('a.c', '+([[:alpha:].])')); 425 | assert(match.isMatch('a.c', '+([[:alpha:].])+([[:alpha:].])')); 426 | assert(match.isMatch('a.c', '*([[:alpha:].])')); 427 | assert(match.isMatch('a.c', '*([[:alpha:].])*([[:alpha:].])')); 428 | assert(match.isMatch('a.c', '?([[:alpha:].])?([[:alpha:].])?([[:alpha:].])')); 429 | assert(match.isMatch('a.c', '@([[:alpha:].])@([[:alpha:].])@([[:alpha:].])')); 430 | assert(!match.isMatch('.', '!(\\.)')); 431 | assert(!match.isMatch('.', '!([[:alpha:].])')); 432 | assert(match.isMatch('.', '?([[:alpha:].])')); 433 | assert(match.isMatch('.', '@([[:alpha:].])')); 434 | }); 435 | 436 | // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob2.tests 437 | it('should pass extglob2 tests', function() { 438 | assert(!match.isMatch('baaac', '*(@(a))a@(c)')); 439 | assert(!match.isMatch('c', '*(@(a))a@(c)')); 440 | assert(!match.isMatch('egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))')); 441 | assert(!match.isMatch('foooofof', '*(f+(o))')); 442 | assert(!match.isMatch('foooofofx', '*(f*(o))')); 443 | assert(!match.isMatch('foooxfooxofoxfooox', '*(f*(o)x)')); 444 | assert(!match.isMatch('ofooofoofofooo', '*(f*(o))')); 445 | assert(!match.isMatch('ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)')); 446 | assert(!match.isMatch('oxfoxfox', '*(oxf+(ox))')); 447 | assert(!match.isMatch('xfoooofof', '*(f*(o))')); 448 | assert(match.isMatch('aaac', '*(@(a))a@(c)')); 449 | assert(match.isMatch('aac', '*(@(a))a@(c)')); 450 | assert(match.isMatch('abbcd', '@(ab|a*(b))*(c)d')); 451 | assert(match.isMatch('abcd', '?@(a|b)*@(c)d')); 452 | assert(match.isMatch('abcd', '@(ab|a*@(b))*(c)d')); 453 | assert(match.isMatch('ac', '*(@(a))a@(c)')); 454 | assert(match.isMatch('acd', '@(ab|a*(b))*(c)d')); 455 | assert(match.isMatch('effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))')); 456 | assert(match.isMatch('efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))')); 457 | assert(match.isMatch('egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))')); 458 | assert(match.isMatch('egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))')); 459 | assert(match.isMatch('fffooofoooooffoofffooofff', '*(*(f)*(o))')); 460 | assert(match.isMatch('ffo', '*(f*(o))')); 461 | assert(match.isMatch('fofo', '*(f*(o))')); 462 | assert(match.isMatch('foofoofo', '@(foo|f|fo)*(f|of+(o))')); 463 | assert(match.isMatch('fooofoofofooo', '*(f*(o))')); 464 | assert(match.isMatch('foooofo', '*(f*(o))')); 465 | assert(match.isMatch('foooofof', '*(f*(o))')); 466 | assert(match.isMatch('foooxfooxfoxfooox', '*(f*(o)x)')); 467 | assert(match.isMatch('foooxfooxfxfooox', '*(f*(o)x)')); 468 | assert(match.isMatch('ofoofo', '*(of+(o))')); 469 | assert(match.isMatch('ofoofo', '*(of+(o)|f)')); 470 | assert(match.isMatch('ofoooxoofxo', '*(*(of*(o)x)o)')); 471 | assert(match.isMatch('ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)')); 472 | assert(match.isMatch('ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)')); 473 | assert(match.isMatch('ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)')); 474 | assert(match.isMatch('ofxoofxo', '*(*(of*(o)x)o)')); 475 | assert(match.isMatch('oofooofo', '*(of|oof+(o))')); 476 | assert(match.isMatch('oxfoxoxfox', '*(oxf+(ox))')); 477 | }); 478 | 479 | it('should support backtracking in alternation matches', function() { 480 | assert(match.isMatch('fofoofoofofoo', '*(fo|foo)')); 481 | }); 482 | 483 | it('should support exclusions', function() { 484 | assert(!match.isMatch('f', '!(f)')); 485 | assert(!match.isMatch('f', '*(!(f))')); 486 | assert(!match.isMatch('f', '+(!(f))')); 487 | assert(!match.isMatch('foo', '!(foo)')); 488 | assert(!match.isMatch('foob', '!(foo)b*')); 489 | assert(!match.isMatch('mad.moo.cow', '!(*.*).!(*.*)')); 490 | assert(!match.isMatch('mucca.pazza', 'mu!(*(c))?.pa!(*(z))?')); 491 | assert(!match.isMatch('zoot', '@(!(z*)|*x)')); 492 | assert(match.isMatch('fff', '!(f)')); 493 | assert(match.isMatch('fff', '*(!(f))')); 494 | assert(match.isMatch('fff', '+(!(f))')); 495 | assert(match.isMatch('foo', '!(f)')); 496 | assert(match.isMatch('foo', '!(x)')); 497 | assert(match.isMatch('foo', '!(x)*')); 498 | assert(match.isMatch('foo', '*(!(f))')); 499 | assert(match.isMatch('foo', '+(!(f))')); 500 | assert(match.isMatch('foobar', '!(foo)')); 501 | assert(match.isMatch('foot', '@(!(z*)|*x)')); 502 | assert(match.isMatch('foox', '@(!(z*)|*x)')); 503 | assert(match.isMatch('ooo', '!(f)')); 504 | assert(match.isMatch('ooo', '*(!(f))')); 505 | assert(match.isMatch('ooo', '+(!(f))')); 506 | assert(match.isMatch('zoox', '@(!(z*)|*x)')); 507 | }); 508 | 509 | it('should pass extglob3 tests', function() { 510 | assert(match.isMatch('ab/../', '+(??)/..?(/)')); 511 | assert(match.isMatch('ab/../', '+(??|a*)/..?(/)')); 512 | assert(match.isMatch('ab/../', '+(?b)/..?(/)')); 513 | assert(match.isMatch('ab/../', '+(?b|?b)/..?(/)')); 514 | assert(match.isMatch('ab/../', '+([!/])/../')); 515 | assert(match.isMatch('ab/../', '+([!/])/..?(/)')); 516 | assert(match.isMatch('ab/../', '+([!/])/..@(/)')); 517 | assert(match.isMatch('ab/../', '+([^/])/../')); 518 | assert(match.isMatch('ab/../', '+([^/])/..?(/)')); 519 | assert(match.isMatch('ab/../', '+(a*)/..?(/)')); 520 | assert(match.isMatch('ab/../', '+(ab)/..?(/)')); 521 | assert(match.isMatch('ab/../', '?(ab)/..?(/)')); 522 | assert(match.isMatch('ab/../', '?(ab|??)/..?(/)')); 523 | assert(match.isMatch('ab/../', '?b/..?(/)')); 524 | assert(match.isMatch('ab/../', '@(??)/..?(/)')); 525 | assert(match.isMatch('ab/../', '@(??|a*)/..?(/)')); 526 | assert(match.isMatch('ab/../', '@(?b|?b)/..?(/)')); 527 | assert(match.isMatch('ab/../', '@(a*)/..?(/)')); 528 | assert(match.isMatch('ab/../', '@(a?|?b)/..?(/)')); 529 | assert(match.isMatch('ab/../', '@(ab|+([!/]))/..?(/)')); 530 | assert(match.isMatch('ab/../', '@(ab|+([^/]))/..?(/)')); 531 | assert(match.isMatch('ab/../', '@(ab|?b)/..?(/)')); 532 | assert(match.isMatch('ab/../', '[!/][!/]/../')); 533 | assert(match.isMatch('ab/../', '[^/][^/]/../')); 534 | assert(match.isMatch('x', '@(x)')); 535 | }); 536 | }); 537 | --------------------------------------------------------------------------------