[Pkg-javascript-commits] [node-extglob] 01/02: Import Upstream version 1.0.0

Sruthi Chandran srud-guest at moszumanska.debian.org
Thu Nov 3 15:14:08 UTC 2016


This is an automated email from the git hooks/post-receive script.

srud-guest pushed a commit to branch master
in repository node-extglob.

commit 474450844ba76b57b03f917341e5b4f94b4edae5
Author: Sruthi <srud at disroot.org>
Date:   Thu Nov 3 20:38:42 2016 +0530

    Import Upstream version 1.0.0
---
 .editorconfig                                 |  13 +
 .eslintrc.json                                | 122 ++++++
 .gitattributes                                |   1 +
 .gitignore                                    |  20 +
 .travis.yml                                   |  11 +
 .verb.md                                      |  84 +++++
 LICENSE                                       |  21 ++
 README.md                                     | 329 ++++++++++++++++
 appveyor.yml                                  |  26 ++
 benchmark/code/isMatch/extglob.js             |   5 +
 benchmark/code/isMatch/minimatch.js           |   5 +
 benchmark/code/match/extglob.js               |   5 +
 benchmark/code/match/minimatch.js             |   5 +
 benchmark/fixtures/isMatch/negation-nested.js |   1 +
 benchmark/fixtures/isMatch/negation-simple.js |   1 +
 benchmark/fixtures/isMatch/range-false.js     |   1 +
 benchmark/fixtures/isMatch/range-true.js      |   1 +
 benchmark/fixtures/isMatch/star-simple.js     |   1 +
 benchmark/fixtures/match/negation-basic.js    |   1 +
 benchmark/fixtures/match/star-basic.js        |   1 +
 benchmark/index.js                            |  36 ++
 benchmark/last.md                             |  36 ++
 benchmark/package.json                        |  18 +
 changelog.md                                  |  18 +
 examples/extglob.create.js                    |   7 +
 examples/extglob.js                           |  12 +
 examples/parser.js                            |  10 +
 examples/regex.js                             |   7 +
 examples/source-map.js                        |   7 +
 gulpfile.js                                   |  32 ++
 index.js                                      | 296 +++++++++++++++
 lib/compilers.js                              | 156 ++++++++
 lib/extglob.js                                |  80 ++++
 lib/parsers.js                                | 156 ++++++++
 lib/utils.js                                  |  64 ++++
 package.json                                  |  99 +++++
 test/bash.extglob.js                          | 199 ++++++++++
 test/bash.extglob1.js                         |  35 ++
 test/bash.extglob1a.js                        |  40 ++
 test/bash.extglob2.js                         |  96 +++++
 test/bash.extglob3.js                         |  51 +++
 test/bash.js                                  | 290 +++++++++++++++
 test/errors.js                                |  66 ++++
 test/fixtures/posix.txt                       | 233 ++++++++++++
 test/negations.js                             | 136 +++++++
 test/options.js                               |  41 ++
 test/reference.js                             |  25 ++
 test/support/compare.js                       |   5 +
 test/support/match.js                         |  60 +++
 test/support/matcher.js                       |  78 ++++
 test/support/parse.js                         |  46 +++
 test/support/try-bash.js                      |  31 ++
 test/support/utils.js                         |  11 +
 test/test.js                                  | 515 ++++++++++++++++++++++++++
 54 files changed, 3646 insertions(+)

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..818e072
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,13 @@
+root = true
+
+[*]
+indent_style = space
+end_of_line = lf
+charset = utf-8
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[{**/{actual,fixtures,expected,templates}/**,*.md}]
+trim_trailing_whitespace = false
+insert_final_newline = false
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..948dbdb
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,122 @@
+{
+  "ecmaFeatures": {
+    "modules": true,
+    "experimentalObjectRestSpread": true
+  },
+
+  "env": {
+    "browser": false,
+    "es6": true,
+    "node": true,
+    "mocha": true
+  },
+
+  "globals": {
+    "document": false,
+    "navigator": false,
+    "window": false
+  },
+
+  "rules": {
+    "accessor-pairs": 2,
+    "arrow-spacing": [2, { "before": true, "after": true }],
+    "block-spacing": [2, "always"],
+    "brace-style": [2, "1tbs", { "allowSingleLine": true }],
+    "comma-dangle": [2, "never"],
+    "comma-spacing": [2, { "before": false, "after": true }],
+    "comma-style": [2, "last"],
+    "constructor-super": 2,
+    "curly": [2, "multi-line"],
+    "dot-location": [2, "property"],
+    "eol-last": 2,
+    "eqeqeq": [2, "allow-null"],
+    "generator-star-spacing": [2, { "before": true, "after": true }],
+    "handle-callback-err": [2, "^(err|error)$" ],
+    "indent": [2, 2, { "SwitchCase": 1 }],
+    "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
+    "keyword-spacing": [2, { "before": true, "after": true }],
+    "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
+    "new-parens": 2,
+    "no-array-constructor": 2,
+    "no-caller": 2,
+    "no-class-assign": 2,
+    "no-cond-assign": 2,
+    "no-const-assign": 2,
+    "no-control-regex": 2,
+    "no-debugger": 2,
+    "no-delete-var": 2,
+    "no-dupe-args": 2,
+    "no-dupe-class-members": 2,
+    "no-dupe-keys": 2,
+    "no-duplicate-case": 2,
+    "no-empty-character-class": 2,
+    "no-eval": 2,
+    "no-ex-assign": 2,
+    "no-extend-native": 2,
+    "no-extra-bind": 2,
+    "no-extra-boolean-cast": 2,
+    "no-extra-parens": [2, "functions"],
+    "no-fallthrough": 2,
+    "no-floating-decimal": 2,
+    "no-func-assign": 2,
+    "no-implied-eval": 2,
+    "no-inner-declarations": [2, "functions"],
+    "no-invalid-regexp": 2,
+    "no-irregular-whitespace": 2,
+    "no-iterator": 2,
+    "no-label-var": 2,
+    "no-labels": 2,
+    "no-lone-blocks": 2,
+    "no-mixed-spaces-and-tabs": 2,
+    "no-multi-spaces": 2,
+    "no-multi-str": 2,
+    "no-multiple-empty-lines": [2, { "max": 1 }],
+    "no-native-reassign": 0,
+    "no-negated-in-lhs": 2,
+    "no-new": 2,
+    "no-new-func": 2,
+    "no-new-object": 2,
+    "no-new-require": 2,
+    "no-new-wrappers": 2,
+    "no-obj-calls": 2,
+    "no-octal": 2,
+    "no-octal-escape": 2,
+    "no-proto": 0,
+    "no-redeclare": 2,
+    "no-regex-spaces": 2,
+    "no-return-assign": 2,
+    "no-self-compare": 2,
+    "no-sequences": 2,
+    "no-shadow-restricted-names": 2,
+    "no-spaced-func": 2,
+    "no-sparse-arrays": 2,
+    "no-this-before-super": 2,
+    "no-throw-literal": 2,
+    "no-trailing-spaces": 0,
+    "no-undef": 2,
+    "no-undef-init": 2,
+    "no-unexpected-multiline": 2,
+    "no-unneeded-ternary": [2, { "defaultAssignment": false }],
+    "no-unreachable": 2,
+    "no-unused-vars": [2, { "vars": "all", "args": "none" }],
+    "no-useless-call": 0,
+    "no-with": 2,
+    "one-var": [0, { "initialized": "never" }],
+    "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
+    "padded-blocks": [0, "never"],
+    "quotes": [2, "single", "avoid-escape"],
+    "radix": 2,
+    "semi": [2, "always"],
+    "semi-spacing": [2, { "before": false, "after": true }],
+    "space-before-blocks": [2, "always"],
+    "space-before-function-paren": [2, "never"],
+    "space-in-parens": [2, "never"],
+    "space-infix-ops": 2,
+    "space-unary-ops": [2, { "words": true, "nonwords": false }],
+    "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
+    "use-isnan": 2,
+    "valid-typeof": 2,
+    "wrap-iife": [2, "any"],
+    "yoda": [2, "never"]
+  }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..a52bd18
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.* text eol=lf
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..13bb9ba
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+# always ignore files
+*.DS_Store
+*.sublime-*
+
+# test related, or directories generated by tests
+test/actual
+actual
+coverage
+
+# npm
+node_modules
+npm-debug.log
+
+# misc
+_gh_pages
+bower_components
+vendor
+temp
+tmp
+TODO.md
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..98b1311
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+sudo: false
+language: node_js
+os:
+  - linux
+  - osx
+node_js:
+  - node
+  - '6'
+  - '5'
+  - '0.12'
+  - '0.10'
diff --git a/.verb.md b/.verb.md
new file mode 100644
index 0000000..a2894fb
--- /dev/null
+++ b/.verb.md
@@ -0,0 +1,84 @@
+- Convert an extglob string to a regex-compatible string. 
+- More complete (and correct) support than [minimatch](https://github.com/isaacs/minimatch) (minimatch fails a large percentage of the extglob tests)
+- Handles [negation patterns](#extglob-patterns)
+- Handles [nested patterns](#extglob-patterns)
+- Organized code base, easy to maintain and make changes when edge cases arise
+- As you can see by the [benchmarks](#benchmarks), extglob doesn't pay with speed for it's completeness, accuracy and quality.
+
+**Heads up!**: This library only supports extglobs, to handle full glob patterns and other extended globbing features use [micromatch][] instead.
+
+## Usage
+
+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.
+
+```js
+var extglob = require('{%= name %}');
+console.log(extglob('!(xyz)*.js'));
+```
+
+## Extglob cheatsheet
+
+Extended globbing patterns can be defined as follows (as described by the [bash man page][bash]):
+
+| **pattern** | **regex equivalent** | **description** |
+| --- | --- | --- |
+| `?(pattern-list)` | `(...|...)?` |  Matches zero or one occurrence of the given pattern(s) |
+| `*(pattern-list)` | `(...|...)*` |  Matches zero or more occurrences of the given pattern(s) |
+| `+(pattern-list)` | `(...|...)+` |  Matches one or more occurrences of the given pattern(s) |
+| `@(pattern-list)` | `(...|...)` [^1] |  Matches one of the given pattern(s) |
+| `!(pattern-list)` | N/A |  Matches anything except one of the given pattern(s) |
+
+[^1]: `@` isn't a RegEx character.
+
+## API
+{%= apidocs("index.js") %}
+
+## Options
+
+Available options are based on the options from Bash (and the option names used in bash).
+
+### options.nullglob 
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+When enabled, the pattern itself will be returned when no matches are found.
+
+### options.nonull 
+
+Alias for [options.nullglob](#optionsnullglob), included for parity with minimatch.
+
+### options.cache 
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+Functions are memoized based on the given glob patterns and options. Disable memoization by setting `options.cache` to false.
+
+### options.failglob 
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+Throw an error is no matches are found.
+
+## Benchmarks
+
+Last run on {%= date() %}
+
+```sh
+{%= docs("benchmark/last.md") %}
+```
+
+## Differences from Bash
+
+This library has complete parity with Bash 4.3 with only a couple of minor differences. 
+
+- 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.
+- 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
+
+[bash]: https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html
+[micromatch]: https://github.com/jonschlinkert/micromatch
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6525171
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2016, Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a93982
--- /dev/null
+++ b/README.md
@@ -0,0 +1,329 @@
+# 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/jonschlinkert/extglob.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/e [...]
+
+> Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save extglob
+```
+
+* Convert an extglob string to a regex-compatible string.
+* More complete (and correct) support than [minimatch](https://github.com/isaacs/minimatch) (minimatch fails a large percentage of the extglob tests)
+* Handles [negation patterns](#extglob-patterns)
+* Handles [nested patterns](#extglob-patterns)
+* Organized code base, easy to maintain and make changes when edge cases arise
+* As you can see by the [benchmarks](#benchmarks), extglob doesn't pay with speed for it's completeness, accuracy and quality.
+
+**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.
+
+## Usage
+
+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.
+
+```js
+var extglob = require('extglob');
+console.log(extglob('!(xyz)*.js'));
+```
+
+## Extglob cheatsheet
+
+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)):
+
+| **pattern** | **regex equivalent** | **description** | 
+| --- | --- | --- |
+| `?(pattern-list)` | `(... | ...)?` | Matches zero or one occurrence of the given pattern(s) |
+| `*(pattern-list)` | `(... | ...)*` | Matches zero or more occurrences of the given pattern(s) |
+| `+(pattern-list)` | `(... | ...)+` | Matches one or more occurrences of the given pattern(s) |
+| `@(pattern-list)` | `(... | ...)` <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> | Matches one of the given pattern(s) |
+| `!(pattern-list)` | N/A | Matches anything except one of the given pattern(s) |
+
+## API
+
+### [extglob](index.js#L37)
+
+Convert the given `extglob` pattern into a regex-compatible string. Returns an object with the compiled result and the parsed AST.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+console.log(extglob('*.!(*a)'));
+//=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?'
+```
+
+**Params**
+
+* `pattern` **{String}**
+* `options` **{Object}**
+* `returns` **{String}**
+
+### [.match](index.js#L68)
+
+Takes an array of strings and an extglob pattern and returns a new array that contains only the strings that match the pattern.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)'));
+//=> ['a.b', 'a.c']
+```
+
+**Params**
+
+* `list` **{Array}**: Array of strings to match
+* `pattern` **{String}**: Extglob pattern
+* `options` **{Object}**
+* `returns` **{Array}**: Returns an array of matches
+
+### [.isMatch](index.js#L123)
+
+Returns true if the specified `string` matches the given extglob `pattern`.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+
+console.log(extglob.isMatch('a.a', '*.!(*a)'));
+//=> false
+console.log(extglob.isMatch('a.b', '*.!(*a)'));
+//=> true
+```
+
+**Params**
+
+* `string` **{String}**: String to match
+* `pattern` **{String}**: Extglob pattern
+* `options` **{String}**
+* `returns` **{Boolean}**
+
+### [.contains](index.js#L162)
+
+Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but the pattern can match any part of the string.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+console.log(extglob.contains('aa/bb/cc', '*b'));
+//=> true
+console.log(extglob.contains('aa/bb/cc', '*d'));
+//=> false
+```
+
+**Params**
+
+* `str` **{String}**: The string to match.
+* `pattern` **{String}**: Glob pattern to use for matching.
+* `options` **{Object}**
+* `returns` **{Boolean}**: Returns true if the patter matches any part of `str`.
+
+### [.matcher](index.js#L196)
+
+Takes an extglob pattern and returns a matcher function. The returned function takes the string to match as its only argument.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+var isMatch = extglob.matcher('*.!(*a)');
+
+console.log(isMatch('a.a'));
+//=> false
+console.log(isMatch('a.b'));
+//=> true
+```
+
+**Params**
+
+* `pattern` **{String}**: Extglob pattern
+* `options` **{String}**
+* `returns` **{Boolean}**
+
+### [.create](index.js#L226)
+
+Convert the given `extglob` pattern into a regex-compatible string. Returns an object with the compiled result and the parsed AST.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+console.log(extglob.create('*.!(*a)').output);
+//=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?'
+```
+
+**Params**
+
+* `str` **{String}**
+* `options` **{Object}**
+* `returns` **{String}**
+
+### [.makeRe](index.js#L255)
+
+Create a regular expression from the given `pattern` and `options`.
+
+**Example**
+
+```js
+var extglob = require('extglob');
+var re = extglob.makeRe('*.!(*a)');
+console.log(re);
+//=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/
+```
+
+**Params**
+
+* `pattern` **{String}**: The pattern to convert to regex.
+* `options` **{Object}**
+* `returns` **{RegExp}**
+
+## Options
+
+Available options are based on the options from Bash (and the option names used in bash).
+
+### options.nullglob
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+When enabled, the pattern itself will be returned when no matches are found.
+
+### options.nonull
+
+Alias for [options.nullglob](#optionsnullglob), included for parity with minimatch.
+
+### options.cache
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+Functions are memoized based on the given glob patterns and options. Disable memoization by setting `options.cache` to false.
+
+### options.failglob
+
+**Type**: `boolean`
+
+**Default**: `undefined`
+
+Throw an error is no matches are found.
+
+## Benchmarks
+
+Last run on October 20, 2016
+
+```sh
+Benchmarking: (5 of 5)
+ · negation-nested
+ · negation-simple
+ · range-false
+ · range-true
+ · star-simple
+
+# benchmark/fixtures/isMatch/negation-nested.js (49 bytes)
+  extglob x 1,988,591 ops/sec ±1.18% (84 runs sampled)
+  minimatch x 73,335 ops/sec ±1.38% (84 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/negation-simple.js (43 bytes)
+  extglob x 2,320,380 ops/sec ±1.71% (86 runs sampled)
+  minimatch x 122,947 ops/sec ±1.28% (86 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/range-false.js (56 bytes)
+  extglob x 1,729,572 ops/sec ±1.22% (84 runs sampled)
+  minimatch x 112,566 ops/sec ±1.26% (85 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/range-true.js (56 bytes)
+  extglob x 1,819,085 ops/sec ±1.28% (83 runs sampled)
+  minimatch x 115,153 ops/sec ±1.50% (85 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/star-simple.js (46 bytes)
+  extglob x 1,970,063 ops/sec ±1.46% (83 runs sampled)
+  minimatch x 138,805 ops/sec ±1.31% (87 runs sampled)
+
+  fastest is extglob
+```
+
+## Differences from Bash
+
+This library has complete parity with Bash 4.3 with only a couple of minor differences.
+
+* 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.
+* 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
+
+## About
+
+### Related projects
+
+* [braces](https://www.npmjs.com/package/braces): Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces… [more](https://github.com/jonschlinkert/braces) | [homepage](https://github.com/jonschlinkert/braces "Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces specification.")
+* [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/jonschlinkert/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.")
+* [expand-range](https://www.npmjs.com/package/expand-range): Fast, bash-like range expansion. Expand a range of numbers or letters, uppercase or lowercase. See… [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. See the benchmarks. Used by micromatch.")
+* [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`")
+* [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/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor**<br/> | 
+| --- | --- | --- | --- | --- |
+| 32 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 2 | [isiahmeadows](https://github.com/isiahmeadows) |
+| 1 | [shinnn](https://github.com/shinnn) |
+
+### Building docs
+
+_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
+
+To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
+
+```sh
+$ npm install -g verb verb-generate-readme && verb
+```
+
+### Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm install -d && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT license](https://github.com/jonschlinkert/extglob/blob/master/LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on October 20, 2016._
+
+<hr class="footnotes-sep">
+<section class="footnotes">
+<ol class="footnotes-list">
+<li id="fn1"  class="footnote-item">`@` isn't a RegEx character. <a href="#fnref1" class="footnote-backref">↩</a>
+
+</li>
+</ol>
+</section>
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..ce44317
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,26 @@
+# Test against this version of Node.js
+environment:
+  matrix:
+    # node.js
+    - nodejs_version: "6.0"
+    - nodejs_version: "5.0"
+    - nodejs_version: "0.12"
+    - nodejs_version: "0.10"
+
+# Install scripts. (runs after repo cloning)
+install:
+  # Get the latest stable version of Node.js or io.js
+  - ps: Install-Product node $env:nodejs_version
+  # install modules
+  - npm install
+
+# Post-install test scripts.
+test_script:
+  # Output useful info for debugging.
+  - node --version
+  - npm --version
+  # run tests
+  - npm test
+
+# Don't actually build.
+build: off
diff --git a/benchmark/code/isMatch/extglob.js b/benchmark/code/isMatch/extglob.js
new file mode 100644
index 0000000..2152e16
--- /dev/null
+++ b/benchmark/code/isMatch/extglob.js
@@ -0,0 +1,5 @@
+var extglob = require('../../..');
+
+module.exports = function(str, pattern) {
+  return extglob.isMatch(str, pattern);
+};
diff --git a/benchmark/code/isMatch/minimatch.js b/benchmark/code/isMatch/minimatch.js
new file mode 100644
index 0000000..c32c292
--- /dev/null
+++ b/benchmark/code/isMatch/minimatch.js
@@ -0,0 +1,5 @@
+var minimatch = require('minimatch');
+
+module.exports = function(str, pattern) {
+  return minimatch(str, pattern);
+};
diff --git a/benchmark/code/match/extglob.js b/benchmark/code/match/extglob.js
new file mode 100644
index 0000000..7db163d
--- /dev/null
+++ b/benchmark/code/match/extglob.js
@@ -0,0 +1,5 @@
+var extglob = require('../../..');
+
+module.exports = function(file, pattern) {
+  return extglob.match(file, pattern);
+};
diff --git a/benchmark/code/match/minimatch.js b/benchmark/code/match/minimatch.js
new file mode 100644
index 0000000..f4e5ee4
--- /dev/null
+++ b/benchmark/code/match/minimatch.js
@@ -0,0 +1,5 @@
+var minimatch = require('minimatch');
+
+module.exports = function(files, pattern) {
+  return minimatch.match(files, pattern);
+};
diff --git a/benchmark/fixtures/isMatch/negation-nested.js b/benchmark/fixtures/isMatch/negation-nested.js
new file mode 100644
index 0000000..6496bf2
--- /dev/null
+++ b/benchmark/fixtures/isMatch/negation-nested.js
@@ -0,0 +1 @@
+module.exports = ['a.b', 'a.!(!(@(*b)))', true];
diff --git a/benchmark/fixtures/isMatch/negation-simple.js b/benchmark/fixtures/isMatch/negation-simple.js
new file mode 100644
index 0000000..d72709a
--- /dev/null
+++ b/benchmark/fixtures/isMatch/negation-simple.js
@@ -0,0 +1 @@
+module.exports = ['a.b', 'a.!(*a)', true];
diff --git a/benchmark/fixtures/isMatch/range-false.js b/benchmark/fixtures/isMatch/range-false.js
new file mode 100644
index 0000000..fa5ea59
--- /dev/null
+++ b/benchmark/fixtures/isMatch/range-false.js
@@ -0,0 +1 @@
+module.exports = ['para381', 'para?([345]|99)1', true];
diff --git a/benchmark/fixtures/isMatch/range-true.js b/benchmark/fixtures/isMatch/range-true.js
new file mode 100644
index 0000000..a6e0f99
--- /dev/null
+++ b/benchmark/fixtures/isMatch/range-true.js
@@ -0,0 +1 @@
+module.exports = ['para991', 'para?([345]|99)1', true];
diff --git a/benchmark/fixtures/isMatch/star-simple.js b/benchmark/fixtures/isMatch/star-simple.js
new file mode 100644
index 0000000..8d99a90
--- /dev/null
+++ b/benchmark/fixtures/isMatch/star-simple.js
@@ -0,0 +1 @@
+module.exports = ['abef', 'ab*(e|f)*', true];
diff --git a/benchmark/fixtures/match/negation-basic.js b/benchmark/fixtures/match/negation-basic.js
new file mode 100644
index 0000000..2181836
--- /dev/null
+++ b/benchmark/fixtures/match/negation-basic.js
@@ -0,0 +1 @@
+module.exports = [['a.a', 'a.b', 'a.a.a', 'c.a', 'a.', 'd.a.d', 'a.bb', 'a.ccc'], 'a.!(*a)', ['d.a.d', 'a.', 'a.b', 'a.bb', 'a.ccc']];
diff --git a/benchmark/fixtures/match/star-basic.js b/benchmark/fixtures/match/star-basic.js
new file mode 100644
index 0000000..94799b3
--- /dev/null
+++ b/benchmark/fixtures/match/star-basic.js
@@ -0,0 +1 @@
+module.exports = [['ab', 'abef'], 'ab*(e|f)'];
diff --git a/benchmark/index.js b/benchmark/index.js
new file mode 100644
index 0000000..6c07dab
--- /dev/null
+++ b/benchmark/index.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var path = require('path');
+var util = require('util');
+var cyan = require('ansi-cyan');
+var argv = require('minimist')(process.argv.slice(2));
+var isPrimitive = require('is-primitive');
+var isObject = require('is-object');
+var Suite = require('benchmarked');
+
+function run(type, pattern) {
+  var suite = new Suite({
+    cwd: __dirname,
+    fixtures: path.join('fixtures', type, '*.js'),
+    code: path.join('code', type, '*.js')
+  });
+
+  if (argv.dry) {
+    console.log(type);
+    suite.dryRun(function(code, fixture) {
+      console.log(cyan('%s > %s'), code.key, fixture.key);
+      var args = require(fixture.path);
+      var last = [];
+      if (args.length > 2) {
+        last = args.pop();
+      }
+      var expected = util.inspect(last, {depth: null});
+      console.log(util.inspect(code.run.apply(null, args), {depth: null}));
+      console.log();
+    });
+  } else {
+    suite.run();
+  }
+}
+
+run(argv._[0] || 'isMatch');
diff --git a/benchmark/last.md b/benchmark/last.md
new file mode 100644
index 0000000..861b78c
--- /dev/null
+++ b/benchmark/last.md
@@ -0,0 +1,36 @@
+Benchmarking: (5 of 5)
+ · negation-nested
+ · negation-simple
+ · range-false
+ · range-true
+ · star-simple
+
+# benchmark/fixtures/isMatch/negation-nested.js (49 bytes)
+  extglob x 1,988,591 ops/sec ±1.18% (84 runs sampled)
+  minimatch x 73,335 ops/sec ±1.38% (84 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/negation-simple.js (43 bytes)
+  extglob x 2,320,380 ops/sec ±1.71% (86 runs sampled)
+  minimatch x 122,947 ops/sec ±1.28% (86 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/range-false.js (56 bytes)
+  extglob x 1,729,572 ops/sec ±1.22% (84 runs sampled)
+  minimatch x 112,566 ops/sec ±1.26% (85 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/range-true.js (56 bytes)
+  extglob x 1,819,085 ops/sec ±1.28% (83 runs sampled)
+  minimatch x 115,153 ops/sec ±1.50% (85 runs sampled)
+
+  fastest is extglob
+
+# benchmark/fixtures/isMatch/star-simple.js (46 bytes)
+  extglob x 1,970,063 ops/sec ±1.46% (83 runs sampled)
+  minimatch x 138,805 ops/sec ±1.31% (87 runs sampled)
+
+  fastest is extglob
diff --git a/benchmark/package.json b/benchmark/package.json
new file mode 100644
index 0000000..22a96d3
--- /dev/null
+++ b/benchmark/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "extglob-benchmarks",
+  "version": "0.0.0",
+  "private": true,
+  "main": "index.js",
+  "scripts": {
+    "test": "mocha"
+  },
+  "devDependencies": {
+    "ansi-cyan": "^0.1.1",
+    "benchmarked": "^0.2.2",
+    "extglob": "^0.3.2",
+    "is-object": "^1.0.1",
+    "is-primitive": "^2.0.0",
+    "minimatch": "^3.0.0",
+    "minimist": "^1.2.0"
+  }
+}
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 0000000..5947fcc
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,18 @@
+## Changelog
+
+### v1.0.0
+
+**Breaking changes**
+
+- The main export now returns the compiled string, instead of the object returned from the compiler
+
+**Added features**
+
+- Adds a `.create` method to do what the main function did before v1.0.0
+
+**Other changes**
+
+- adds `expand-brackets` parsers/compilers to handle nested brackets and extglobs
+- uses `to-regex` to build regex for `makeRe` method
+- improves coverage
+- optimizations
\ No newline at end of file
diff --git a/examples/extglob.create.js b/examples/extglob.create.js
new file mode 100644
index 0000000..24a2305
--- /dev/null
+++ b/examples/extglob.create.js
@@ -0,0 +1,7 @@
+'use strict';
+
+var extglob = require('..');
+
+console.log(extglob.create('*.!(*a)'));
+console.log(extglob.create('*(*(of*(a)x)z)'));
+
diff --git a/examples/extglob.js b/examples/extglob.js
new file mode 100644
index 0000000..4a861d4
--- /dev/null
+++ b/examples/extglob.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var extglob = require('..');
+
+console.log(extglob('!(xyz)*.js'));
+//=>'(?:(?!(?:xyz)).*?)(?!\.).*?\.js'
+
+console.log(extglob('*.!(*a)'));
+//=> '(?!\.).*?\.(?:(?!(?:(?!\.).*?a$)).*?)'
+
+console.log(extglob('+(*(of*(a)x)z)'));
+//=> '+((of(a)*x)*z)*'
diff --git a/examples/parser.js b/examples/parser.js
new file mode 100644
index 0000000..aed7531
--- /dev/null
+++ b/examples/parser.js
@@ -0,0 +1,10 @@
+'use strict';
+
+var parsers = require('../lib/parsers');
+var Extglob = require('../lib/extglob');
+var extglob = new Extglob();
+extglob.use(parsers);
+
+var pattern = '*(*(of*(a)x)z)';
+var res = extglob.parse(pattern);
+console.log(res);
diff --git a/examples/regex.js b/examples/regex.js
new file mode 100644
index 0000000..530eb6b
--- /dev/null
+++ b/examples/regex.js
@@ -0,0 +1,7 @@
+
+var extglob = require('..');
+
+var re = extglob.makeRe('*(a|b|c)');
+console.log(re);
+console.log(re.test('bar'));
+console.log(re.test('bbbaaaccc'));
diff --git a/examples/source-map.js b/examples/source-map.js
new file mode 100644
index 0000000..e8b008e
--- /dev/null
+++ b/examples/source-map.js
@@ -0,0 +1,7 @@
+'use strict';
+
+var extglob = require('..');
+var pattern = '*(*(of*(a)x)z)';
+
+console.log(extglob.create(pattern));
+console.log(extglob.create(pattern, {sourcemap: true}));
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..857a63e
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,32 @@
+'use strict';
+
+var gulp = require('gulp');
+var mocha = require('gulp-mocha');
+var unused = require('gulp-unused');
+var istanbul = require('gulp-istanbul');
+var eslint = require('gulp-eslint');
+
+gulp.task('coverage', function() {
+  return gulp.src(['index.js', 'lib/*.js'])
+    .pipe(istanbul())
+    .pipe(istanbul.hookRequire());
+});
+
+gulp.task('test', ['coverage'], function() {
+  return gulp.src('test/*.js')
+    .pipe(mocha({reporter: 'spec'}))
+    .pipe(istanbul.writeReports());
+});
+
+gulp.task('lint', function() {
+  return gulp.src(['*.js', 'lib/*.js', 'test/{*,support/*}.js'])
+    .pipe(eslint())
+    .pipe(eslint.format());
+});
+
+gulp.task('unused', function() {
+  return gulp.src(['index.js', 'lib/*.js'])
+    .pipe(unused({keys: Object.keys(require('./lib/utils.js'))}));
+});
+
+gulp.task('default', ['test', 'lint']);
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..5c0e43f
--- /dev/null
+++ b/index.js
@@ -0,0 +1,296 @@
+'use strict';
+
+/**
+ * Module dependencies
+ */
+
+var debug = require('debug')('extglob');
+var extend = require('extend-shallow');
+var unique = require('array-unique');
+var toRegex = require('to-regex');
+
+/**
+ * Local dependencies
+ */
+
+var compilers = require('./lib/compilers');
+var parsers = require('./lib/parsers');
+var Extglob = require('./lib/extglob');
+var utils = require('./lib/utils');
+var MAX_LENGTH = 1024 * 64;
+
+/**
+ * Convert the given `extglob` pattern into a regex-compatible string. Returns
+ * an object with the compiled result and the parsed AST.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * console.log(extglob('*.!(*a)'));
+ * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?'
+ * ```
+ * @param {String} `pattern`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+function extglob(pattern, options) {
+  debug('initializing from <%s>', __filename);
+  var res = extglob.create(pattern, options);
+  return res.output;
+}
+
+/**
+ * Cache
+ */
+
+extglob.cache = utils.cache;
+extglob.clearCache = function() {
+  extglob.cache.__data__ = {};
+};
+
+/**
+ * Takes an array of strings and an extglob pattern and returns a new
+ * array that contains only the strings that match the pattern.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)'));
+ * //=> ['a.b', 'a.c']
+ * ```
+ * @param {Array} `list` Array of strings to match
+ * @param {String} `pattern` Extglob pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of matches
+ * @api public
+ */
+
+extglob.match = function(list, pattern, options) {
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected pattern to be a string');
+  }
+
+  list = utils.arrayify(list);
+  var isMatch = extglob.matcher(pattern, options);
+  var len = list.length;
+  var idx = -1;
+  var matches = [];
+
+  while (++idx < len) {
+    var ele = list[idx];
+
+    if (isMatch(ele)) {
+      matches.push(ele);
+    }
+  }
+
+  // if no options were passed, uniquify results and return
+  if (typeof options === 'undefined') {
+    return unique(matches);
+  }
+
+  if (matches.length === 0) {
+    if (options.failglob === true) {
+      throw new Error('no matches found for "' + pattern + '"');
+    }
+    if (options.nonull === true || options.nullglob === true) {
+      return [pattern.split('\\').join('')];
+    }
+  }
+
+  return options.nodupes !== false ? unique(matches) : matches;
+};
+
+/**
+ * Returns true if the specified `string` matches the given
+ * extglob `pattern`.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ *
+ * console.log(extglob.isMatch('a.a', '*.!(*a)'));
+ * //=> false
+ * console.log(extglob.isMatch('a.b', '*.!(*a)'));
+ * //=> true
+ * ```
+ * @param {String} `string` String to match
+ * @param {String} `pattern` Extglob pattern
+ * @param {String} `options`
+ * @return {Boolean}
+ * @api public
+ */
+
+extglob.isMatch = function(str, pattern, options) {
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected pattern to be a string');
+  }
+
+  if (typeof str !== 'string') {
+    throw new TypeError('expected a string');
+  }
+
+  if (pattern === str) {
+    return true;
+  }
+
+  if (pattern === '' || pattern === ' ' || pattern === '.') {
+    return pattern === str;
+  }
+
+  var isMatch = utils.memoize('isMatch', pattern, options, extglob.matcher);
+  return isMatch(str);
+};
+
+/**
+ * Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but
+ * the pattern can match any part of the string.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * console.log(extglob.contains('aa/bb/cc', '*b'));
+ * //=> true
+ * console.log(extglob.contains('aa/bb/cc', '*d'));
+ * //=> false
+ * ```
+ * @param {String} `str` The string to match.
+ * @param {String} `pattern` Glob pattern to use for matching.
+ * @param {Object} `options`
+ * @return {Boolean} Returns true if the patter matches any part of `str`.
+ * @api public
+ */
+
+extglob.contains = function(str, pattern, options) {
+  if (typeof str !== 'string') {
+    throw new TypeError('expected a string');
+  }
+
+  if (pattern === '' || pattern === ' ' || pattern === '.') {
+    return pattern === str;
+  }
+
+  var opts = extend({}, options, {contains: true});
+  opts.strictClose = false;
+  opts.strictOpen = false;
+  return extglob.isMatch(str, pattern, opts);
+};
+
+/**
+ * Takes an extglob pattern and returns a matcher function. The returned
+ * function takes the string to match as its only argument.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * var isMatch = extglob.matcher('*.!(*a)');
+ *
+ * console.log(isMatch('a.a'));
+ * //=> false
+ * console.log(isMatch('a.b'));
+ * //=> true
+ * ```
+ * @param {String} `pattern` Extglob pattern
+ * @param {String} `options`
+ * @return {Boolean}
+ * @api public
+ */
+
+extglob.matcher = function(pattern, options) {
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected pattern to be a string');
+  }
+
+  function matcher() {
+    var re = extglob.makeRe(pattern, options);
+    return function(str) {
+      return re.test(str);
+    };
+  }
+
+  return utils.memoize('matcher', pattern, options, matcher);
+};
+
+/**
+ * Convert the given `extglob` pattern into a regex-compatible string. Returns
+ * an object with the compiled result and the parsed AST.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * console.log(extglob.create('*.!(*a)').output);
+ * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?'
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+extglob.create = function(pattern, options) {
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected pattern to be a string');
+  }
+
+  function create() {
+    var ext = new Extglob(options);
+    var ast = ext.parse(pattern, options);
+    return ext.compile(ast, options);
+  }
+
+  return utils.memoize('create', pattern, options, create);
+};
+
+/**
+ * Create a regular expression from the given `pattern` and `options`.
+ *
+ * ```js
+ * var extglob = require('extglob');
+ * var re = extglob.makeRe('*.!(*a)');
+ * console.log(re);
+ * //=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/
+ * ```
+ * @param {String} `pattern` The pattern to convert to regex.
+ * @param {Object} `options`
+ * @return {RegExp}
+ * @api public
+ */
+
+extglob.makeRe = function(pattern, options) {
+  if (pattern instanceof RegExp) {
+    return pattern;
+  }
+
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected pattern to be a string');
+  }
+
+  if (pattern.length > MAX_LENGTH) {
+    throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
+  }
+
+  function makeRe() {
+    var opts = extend({strictErrors: false}, options);
+    if (opts.strictErrors === true) opts.strict = true;
+    var res = extglob.create(pattern, opts);
+    return toRegex(res.output, opts);
+  }
+
+  var regex = utils.memoize('makeRe', pattern, options, makeRe);
+  if (regex.source.length > MAX_LENGTH) {
+    throw new SyntaxError('potentially malicious regex detected');
+  }
+
+  return regex;
+};
+
+/**
+ * Expose `Extglob` constructor, parsers and compilers
+ */
+
+extglob.Extglob = Extglob;
+extglob.compilers = compilers;
+extglob.parsers = parsers;
+
+/**
+ * Expose `extglob`
+ * @type {Function}
+ */
+
+module.exports = extglob;
diff --git a/lib/compilers.js b/lib/compilers.js
new file mode 100644
index 0000000..2d3c034
--- /dev/null
+++ b/lib/compilers.js
@@ -0,0 +1,156 @@
+'use strict';
+
+var brackets = require('expand-brackets');
+
+/**
+ * Extglob compilers
+ */
+
+module.exports = function(extglob) {
+  var star = extglob.options.star || '.*?';
+
+  /**
+   * Use `expand-brackets` compilers
+   */
+
+  extglob.use(brackets.compilers);
+  extglob.compiler
+
+    /**
+     * Escaped: "\\*"
+     */
+
+    .set('escape', function(node) {
+      return this.emit(node.val, node);
+    })
+
+    /**
+     * Dot: "."
+     */
+
+    .set('dot', function(node) {
+      return this.emit('\\' + node.val, node);
+    })
+
+    /**
+     * Question mark: "?"
+     */
+
+    .set('qmark', function(node) {
+      var val = '[^\\\\/.]';
+      var prev = this.prev();
+
+      if (node.parsed.slice(-1) === '(') {
+        var ch = node.rest.charAt(0);
+        if (ch !== '!' && ch !== '=' && ch !== ':') {
+          return this.emit(val, node);
+        }
+        return this.emit(node.val, node);
+      }
+
+      if (prev.type === 'text' && prev.val) {
+        return this.emit(val, node);
+      }
+
+      if (node.val.length > 1) {
+        val += '{' + node.val.length + '}';
+      }
+      return this.emit(val, node);
+    })
+
+    /**
+     * Plus: "+"
+     */
+
+    .set('plus', function(node) {
+      var prev = node.parsed.slice(-1);
+      if (prev === ']' || prev === ')') {
+        return this.emit(node.val, node);
+      }
+      var ch = this.output.slice(-1);
+      if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) {
+        return this.emit('\\+', node);
+      }
+      if (/\w/.test(ch) && !node.inside) {
+        return this.emit('+\\+?', node);
+      }
+      return this.emit('+', node);
+    })
+
+    /**
+     * Star: "*"
+     */
+
+    .set('star', function(node) {
+      var prev = this.prev();
+      var prefix = prev.type !== 'text' && prev.type !== 'escape'
+        ? '(?!\\.)'
+        : '';
+
+      return this.emit(prefix + star, node);
+    })
+
+    /**
+     * Parens
+     */
+
+    .set('paren', function(node) {
+      node.prev = this.prev();
+      return this.mapVisit(node.nodes);
+    })
+    .set('paren.open', function(node) {
+      switch (node.parent.prefix) {
+        case '!':
+        case '^':
+          return this.emit('(?:(?!(?:', node);
+        case '@':
+        case '+':
+        case '*':
+        case '?':
+          return this.emit('(', node);
+        default: {
+          return this.emit(node.val, node);
+        }
+      }
+    })
+    .set('paren.close', function(node) {
+      if (!node.parent.prev) {
+        return this.emit('\\' + node.val, node);
+      }
+
+      switch (node.prefix) {
+        case '!':
+        case '^':
+          var rest = node.rest;
+          var val = !this.options.bash
+            ? '))' + star + ')'
+            : ').)*$)';
+
+          var boundary = !rest || /^[).]/.test(rest);
+          if (!node.suffix && !this.options.bash && boundary) {
+            val = '$' + val;
+          }
+
+          return this.emit(val, node);
+        case '+':
+          return this.emit(')+', node);
+        case '*':
+          return this.emit(')*', node);
+        case '?':
+          return this.emit(')?', node);
+        case '@':
+        default: {
+          return this.emit(')', node);
+        }
+      }
+    })
+
+    /**
+     * Text
+     */
+
+    .set('text', function(node) {
+      var val = node.val.replace(/[\[\]]/g, '\\$&');
+      return this.emit(val, node);
+    });
+};
diff --git a/lib/extglob.js b/lib/extglob.js
new file mode 100644
index 0000000..d36badd
--- /dev/null
+++ b/lib/extglob.js
@@ -0,0 +1,80 @@
+'use strict';
+
+/**
+ * Module dependencies
+ */
+
+var Snapdragon = require('snapdragon');
+var define = require('define-property');
+var extend = require('extend-shallow');
+var debug = require('debug')('extglob');
+
+/**
+ * Local dependencies
+ */
+
+var compilers = require('./compilers');
+var parsers = require('./parsers');
+
+/**
+ * Customize Snapdragon parser and renderer
+ */
+
+function Extglob(options) {
+  debug('initializing from <%s>', __filename);
+  this.options = extend({source: 'extglob'}, options);
+  this.snapdragon = this.options.snapdragon || new Snapdragon(this.options);
+  this.snapdragon.patterns = this.snapdragon.patterns || {};
+  this.compiler = this.snapdragon.compiler;
+  this.parser = this.snapdragon.parser;
+
+  compilers(this.snapdragon);
+  parsers(this.snapdragon);
+
+  /**
+   * Override Snapdragon `.parse` method
+   */
+
+  define(this.snapdragon, 'parse', function(str, options) {
+    var parsed = Snapdragon.prototype.parse.apply(this, arguments);
+    parsed.input = str;
+
+    // escape unmatched brace/bracket/parens
+    var last = this.parser.stack.pop();
+    if (last && this.options.strict !== true) {
+      var node = last.nodes[0];
+      node.val = '\\' + node.val;
+      var sibling = node.parent.nodes[1];
+      if (sibling.type === 'star') {
+        sibling.loose = true;
+      }
+    }
+
+    // add non-enumerable parser reference
+    define(parsed, 'parser', this.parser);
+    return parsed;
+  });
+
+  /**
+   * Decorate `.parse` method
+   */
+
+  define(this, 'parse', function(ast, options) {
+    return this.snapdragon.parse.apply(this.snapdragon, arguments);
+  });
+
+  /**
+   * Decorate `.compile` method
+   */
+
+  define(this, 'compile', function(ast, options) {
+    return this.snapdragon.compile.apply(this.snapdragon, arguments);
+  });
+
+}
+
+/**
+ * Expose `Extglob`
+ */
+
+module.exports = Extglob;
diff --git a/lib/parsers.js b/lib/parsers.js
new file mode 100644
index 0000000..2ba7352
--- /dev/null
+++ b/lib/parsers.js
@@ -0,0 +1,156 @@
+'use strict';
+
+var brackets = require('expand-brackets');
+var define = require('define-property');
+var utils = require('./utils');
+
+/**
+ * Characters to use in text regex (we want to "not" match
+ * characters that are matched by other parsers)
+ */
+
+var TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+';
+var not = utils.createRegex(TEXT_REGEX);
+
+/**
+ * Extglob parsers
+ */
+
+function parsers(extglob) {
+  extglob.state = extglob.state || {};
+
+  /**
+   * Use `expand-brackets` parsers
+   */
+
+  extglob.use(brackets.parsers);
+  extglob.parser.sets.paren = extglob.parser.sets.paren || [];
+  extglob.parser
+
+    /**
+     * Extglob open: "*("
+     */
+
+    .capture('paren.open', function() {
+      var parsed = this.parsed;
+      var pos = this.position();
+      var m = this.match(/^([!@*?+])?\(/);
+      if (!m) return;
+
+      var prev = this.prev();
+      var prefix = m[1];
+      var val = m[0];
+
+      var open = pos({
+        type: 'paren.open',
+        parsed: parsed,
+        val: val
+      });
+
+      var node = pos({
+        type: 'paren',
+        prefix: prefix,
+        nodes: [open]
+      });
+
+      // if nested negation extglobs, just cancel them out to simplify
+      if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') {
+        prev.prefix = '@';
+        node.prefix = '@';
+      }
+
+      define(node, 'rest', this.input);
+      define(node, 'parsed', parsed);
+      define(node, 'parent', prev);
+      define(open, 'parent', node);
+
+      this.push('paren', node);
+      prev.nodes.push(node);
+    })
+
+    /**
+     * Extglob close: ")"
+     */
+
+    .capture('paren.close', function() {
+      var parsed = this.parsed;
+      var pos = this.position();
+      var m = this.match(/^\)/);
+      if (!m) return;
+
+      var parent = this.pop('paren');
+      var node = pos({
+        type: 'paren.close',
+        rest: this.input,
+        parsed: parsed,
+        val: m[0]
+      });
+
+      if (!this.isType(parent, 'paren')) {
+        if (this.options.strict) {
+          throw new Error('missing opening paren: "("');
+        }
+        node.escaped = true;
+        return node;
+      }
+
+      node.prefix = parent.prefix;
+      parent.nodes.push(node);
+      define(node, 'parent', parent);
+    })
+
+    /**
+     * Escape: "\\."
+     */
+
+    .capture('escape', function() {
+      var pos = this.position();
+      var m = this.match(/^\\(.)/);
+      if (!m) return;
+
+      return pos({
+        type: 'escape',
+        val: m[0],
+        ch: m[1]
+      });
+    })
+
+    /**
+     * Question marks: "?"
+     */
+
+    .capture('qmark', function() {
+      var parsed = this.parsed;
+      var pos = this.position();
+      var m = this.match(/^\?+(?!\()/);
+      if (!m) return;
+      extglob.state.metachar = true;
+      return pos({
+        type: 'qmark',
+        rest: this.input,
+        parsed: parsed,
+        val: m[0]
+      });
+    })
+
+    /**
+     * Character parsers
+     */
+
+    .capture('star', /^\*(?!\()/)
+    .capture('plus', /^\+(?!\()/)
+    .capture('dot', /^\./)
+    .capture('text', not);
+};
+
+/**
+ * Expose text regex string
+ */
+
+module.exports.TEXT_REGEX = TEXT_REGEX;
+
+/**
+ * Extglob parsers
+ */
+
+module.exports = parsers;
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..c5e1414
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,64 @@
+'use strict';
+
+var regex = require('regex-not');
+var Cache = require('fragment-cache');
+var utils = module.exports;
+var cache = utils.cache = new Cache();
+
+/**
+ * Cast `val` to an array
+ * @return {Array}
+ */
+
+utils.arrayify = function(val) {
+  if (!Array.isArray(val)) {
+    return [val];
+  }
+  return val;
+};
+
+/**
+ * Memoize a generated regex or function
+ */
+
+utils.memoize = function(type, pattern, options, fn) {
+  var key = utils.createKey(type + pattern, options);
+
+  if (cache.has(type, key)) {
+    return cache.get(type, key);
+  }
+
+  var val = fn(pattern, options);
+  if (options && options.cache === false) {
+    return val;
+  }
+
+  cache.set(type, key, val);
+  return val;
+};
+
+/**
+ * Create the key to use for memoization. The key is generated
+ * by iterating over the options and concatenating key-value pairs
+ * to the pattern string.
+ */
+
+utils.createKey = function(pattern, options) {
+  var key = pattern;
+  if (typeof options === 'undefined') {
+    return key;
+  }
+  for (var prop in options) {
+    key += ';' + prop + '=' + String(options[prop]);
+  }
+  return key;
+};
+
+/**
+ * Create the regex to use for matching text
+ */
+
+utils.createRegex = function(str) {
+  var opts = {contains: true, strictClose: false};
+  return regex(str, opts);
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b8acbc2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,99 @@
+{
+  "name": "extglob",
+  "description": "Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.",
+  "version": "1.0.0",
+  "homepage": "https://github.com/jonschlinkert/extglob",
+  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
+  "contributors": [
+    "Isiah Meadows <me at isiahmeadows.com> (https://www.isiahmeadows.com)",
+    "Jon Schlinkert <jon.schlinkert at sellside.com> (http://twitter.com/jonschlinkert)",
+    "Shinnosuke Watanabe <snnskwtnb at gmail.com> (https://shinnn.github.io)"
+  ],
+  "repository": "jonschlinkert/extglob",
+  "bugs": {
+    "url": "https://github.com/jonschlinkert/extglob/issues"
+  },
+  "license": "MIT",
+  "files": [
+    "index.js",
+    "lib"
+  ],
+  "main": "index.js",
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "mocha"
+  },
+  "dependencies": {
+    "array-unique": "^0.3.2",
+    "debug": "^2.2.0",
+    "define-property": "^0.2.5",
+    "expand-brackets": "^2.0.1",
+    "extend-shallow": "^2.0.1",
+    "fragment-cache": "^0.2.0",
+    "regex-not": "^1.0.0",
+    "snapdragon": "^0.8.1",
+    "to-regex": "^2.1.0"
+  },
+  "devDependencies": {
+    "bash-match": "^0.1.1",
+    "for-own": "^0.1.4",
+    "gulp": "^3.9.1",
+    "gulp-eslint": "^3.0.1",
+    "gulp-format-md": "^0.1.11",
+    "gulp-istanbul": "^1.1.1",
+    "gulp-mocha": "^3.0.1",
+    "gulp-unused": "^0.2.0",
+    "helper-changelog": "^0.3.0",
+    "is-windows": "^0.2.0",
+    "micromatch": "^2.3.11",
+    "minimatch": "^3.0.3",
+    "mocha": "^3.1.2",
+    "multimatch": "^2.1.0",
+    "yargs-parser": "^4.0.2"
+  },
+  "keywords": [
+    "bash",
+    "extended",
+    "extglob",
+    "glob",
+    "globbing",
+    "ksh",
+    "match",
+    "pattern",
+    "patterns",
+    "regex",
+    "test",
+    "wildcard"
+  ],
+  "verb": {
+    "toc": false,
+    "layout": "default",
+    "tasks": [
+      "readme"
+    ],
+    "related": {
+      "list": [
+        "braces",
+        "expand-brackets",
+        "expand-range",
+        "fill-range",
+        "micromatch"
+      ]
+    },
+    "helpers": [
+      "helper-changelog"
+    ],
+    "plugins": [
+      "gulp-format-md"
+    ],
+    "lint": {
+      "reflinks": true
+    },
+    "reflinks": [
+      "verb",
+      "verb-generate-readme"
+    ]
+  }
+}
diff --git a/test/bash.extglob.js b/test/bash.extglob.js
new file mode 100644
index 0000000..ef15cff
--- /dev/null
+++ b/test/bash.extglob.js
@@ -0,0 +1,199 @@
+'use strict';
+
+require('mocha');
+var match = require('./support/match');
+var assert = require('assert');
+
+// ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob.tests
+describe('bash tests derived from the pd-ksh test suite:', function() {
+  var startLine = 11;
+  var tests = [
+    [ '/dev/udp/129.22.8.102/45', '/dev/@(tcp|udp)/*/*', true ],
+    '',
+    'valid numbers',
+    [ '0', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees
+    [ '12', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees
+    [ '12abc', '0|[1-9]*([0-9])', false ],
+    [ '1', '0|[1-9]*([0-9])', true ], // Bash 4.3 disagrees
+    '',
+    'octal numbers',
+    [ '07', '+([0-7])', true ],
+    [ '0377', '+([0-7])', true ],
+    [ '09', '+([0-7])', false ],
+    '',
+    'stuff from korn\'s book',
+    [ 'paragraph', 'para@(chute|graph)', true ],
+    [ 'paramour', 'para@(chute|graph)', false ],
+    [ 'para991', 'para?([345]|99)1', true ],
+    [ 'para381', 'para?([345]|99)1', false ],
+    [ 'paragraph', 'para*([0-9])', false ],
+    [ 'para', 'para*([0-9])', true ],
+    [ 'para13829383746592', 'para*([0-9])', true ],
+    [ 'paragraph', 'para*([0-9])', false ],
+    [ 'para', 'para+([0-9])', false ],
+    [ 'para987346523', 'para+([0-9])', true ],
+    [ 'paragraph', 'para!(*.[0-9])', true ],
+    [ 'para.38', 'para!(*.[0-9])', true ],
+    [ 'para.graph', 'para!(*.[0-9])', true ],
+    [ 'para39', 'para!(*.[0-9])', true ],
+    '',
+    'tests derived from those  rosenblatt\'s korn shell book',
+    [ '', '*(0|1|3|5|7|9)', true ],
+    [ '""', '*(0|1|3|5|7|9)', false ],
+    [ '137577991', '*(0|1|3|5|7|9)', true ],
+    [ '2468', '*(0|1|3|5|7|9)', false ],
+    [ 'file.c', '*.c?(c)', true ],
+    [ 'file.C', '*.c?(c)', false ],
+    [ 'file.cc', '*.c?(c)', true ],
+    [ 'file.ccc', '*.c?(c)', false ],
+    [ 'parse.y', '!(*.c|*.h|Makefile.in|config*|README)', true ],
+    [ 'shell.c', '!(*.c|*.h|Makefile.in|config*|README)', false ],
+    [ 'Makefile', '!(*.c|*.h|Makefile.in|config*|README)', true ],
+    [ '"VMS.FILE;1"', '*\\;[1-9]*([0-9])', false ],
+    [ '"VMS.FILE;0"', '*\\;[1-9]*([0-9])', false ],
+    [ '"VMS.FILE;"', '*\\;[1-9]*([0-9])', false ],
+    [ '"VMS.FILE;139"', '*\\;[1-9]*([0-9])', false ],
+    [ '"VMS.FILE;139"', '*;[1-9]*([0-9])', false ],
+    [ '"VMS.FILE;139"', '*;[1-9]*([0-9])*', true ],
+    [ '"VMS.FILE;139"', '*;[1-9]**([0-9])*', true ],
+    [ '"VMS.FILE;1N"', '*;[1-9]*([0-9])', false ],
+    '',
+    ['abcx', '!([*)*', true],
+    ['abcx', '!(\\[*)*', true],
+    ['abcz', '!([*)*', true],
+    ['abcz', '!(\\[*)*', true],
+    ['bbc', '!([*)*', true],
+    ['bbc', '!(\\[*)*', true],
+    ['abcx', '+(a|b[)*', true],
+    ['abcx', '+(a|b\\[)*', true],
+    ['abcz', '+(a|b[)*', true],
+    ['abcz', '+(a|b\\[)*', true],
+    ['bbc', '+(a|b[)*', false],
+    ['abcx', '[a*(]*z', false],
+    ['abcx', '[a*\\(]*z', false],
+    ['abcz', '[a*(]*z', true],
+    ['abcz', '[a*\\(]*z', true],
+    ['bbc', '[a*(]*z', false],
+    ['bbc', '[a*\\(]*z', false],
+    '',
+    ['abc', '+()c', false],
+    ['abc', '+()x', false],
+    ['abc', '+(*)c', true],
+    ['abc', '+(*)x', false],
+    ['abc', 'no-file+(a|b)stuff', false],
+    ['abc', 'no-file+(a*(c)|b)stuff', false],
+    '',
+    ['abc', 'a+(b|c)d', false],
+    ['abd', 'a+(b|c)d', true],
+    ['acd', 'a+(b|c)d', true],
+    '',
+    ['abc', 'a!(@(b|B))d', false],
+    ['abd', 'a!(@(b|B))d', false],
+    ['acd', 'a!(@(b|B))d', true],
+    '',
+    ['abc', 'a[b*(foo|bar)]d', false],
+    ['abd', 'a[b*(foo|bar)]d', true],
+    ['acd', 'a[b*(foo|bar)]d', false],
+    '',
+    'simple kleene star tests',
+    ['foo', '*(a|b[)', false],
+    ['foo', '*(a|b[)|f*', true],
+    'this doesn\'t work right yet (from bash notes, it does work in extglob)',
+    ['*(a|b[)', '*(a|b[)', true],
+    '',
+    'More tests derived from a bug report concerning extended glob patterns following a *',
+    ['ab', 'ab*(e|f)', true],
+    ['abcdef', 'ab*(e|f)', false],
+    ['abcfef', 'ab*(e|f)', false],
+    ['abcfefg', 'ab*(e|f)', false],
+    ['abef', 'ab*(e|f)', true],
+    '',
+    ['ab', 'b?*(e|f)', false],
+    ['abcdef', 'b?*(e|f)', false],
+    ['abcfef', 'b?*(e|f)', false],
+    ['abcfefg', 'b?*(e|f)', false],
+    ['abef', 'b?*(e|f)', false],
+    '',
+    ['ab', 'ab*d+(e|f)', false],
+    ['abcdef', 'ab*d+(e|f)', true],
+    ['abcfef', 'ab*d+(e|f)', false],
+    ['abcfefg', 'ab*d+(e|f)', false],
+    ['abef', 'ab*d+(e|f)', false],
+    '',
+    ['ab', 'ab**(e|f)', true],
+    ['abcdef', 'ab**(e|f)', true],
+    ['abcfef', 'ab**(e|f)', true],
+    ['abcfefg', 'ab**(e|f)', true],
+    ['abef', 'ab**(e|f)', true],
+    '',
+    ['ab', 'ab*+(e|f)', false],
+    ['abcdef', 'ab*+(e|f)', true],
+    ['abcfef', 'ab*+(e|f)', true],
+    ['abcfefg', 'ab*+(e|f)', false],
+    ['abef', 'ab*+(e|f)', true],
+    '',
+    ['ab', 'ab**(e|f)', true],
+    ['abcdef', 'ab**(e|f)', true],
+    ['abcfef', 'ab**(e|f)', true],
+    ['abcfefg', 'ab**(e|f)', true],
+    ['abef', 'ab**(e|f)', true],
+    '',
+    ['ab', 'ab**(e|f)g', false],
+    ['abcdef', 'ab**(e|f)g', false],
+    ['abcfef', 'ab**(e|f)g', false],
+    ['abcfefg', 'ab**(e|f)g', true],
+    ['abef', 'ab**(e|f)g', false],
+    '',
+    ['ab', 'ab*+(e|f)', false],
+    ['abcdef', 'ab*+(e|f)', true],
+    ['abcfef', 'ab*+(e|f)', true],
+    ['abcfefg', 'ab*+(e|f)', false],
+    ['abef', 'ab*+(e|f)', true],
+    '',
+    ['ab', 'ab***ef', false],
+    ['abcdef', 'ab***ef', true],
+    ['abcfef', 'ab***ef', true],
+    ['abcfefg', 'ab***ef', false],
+    ['abef', 'ab***ef', true],
+    '',
+    ['ab', 'ab**', true],
+    ['abcdef', 'ab**', true],
+    ['abcfef', 'ab**', true],
+    ['abcfefg', 'ab**', true],
+    ['abef', 'ab**', true],
+    '',
+    'bug in all versions up to and including bash-2.05b',
+    ['123abc', '*?(a)bc', true],
+    '',
+    'character classes',
+    [['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']],
+    [['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']],
+    [['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']],
+    [['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']],
+    [['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', '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'], 'a@([^x])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+([^[:alnum:]])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'], 'a@(.|[^[:alnum:]])b', ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']],
+    '',
+    ['a , b', '*([[:space:]]),*([[:space:]])', false],
+    ['a , b', 'a*([[:space:]]),*([[:space:]])b', true]
+  ];
+
+  tests.forEach(function(test, i) {
+    if (!Array.isArray(test) || !test) return;
+    var fixture = test[0];
+    var pattern = test[1];
+    // if (pattern !== 'a[-.,:\;\ _]b') return;
+    var expected = test[2];
+    var msg = ') should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it((startLine + i) + msg, function() {
+      if (Array.isArray(fixture)) {
+        match(fixture, pattern, expected, msg);
+      } else {
+        assert.equal(match.isMatch(fixture, pattern), expected, msg);
+      }
+    });
+  });
+});
diff --git a/test/bash.extglob1.js b/test/bash.extglob1.js
new file mode 100644
index 0000000..6bc13af
--- /dev/null
+++ b/test/bash.extglob1.js
@@ -0,0 +1,35 @@
+'use strict';
+
+require('mocha');
+var match = require('./support/match');
+var assert = require('assert');
+
+// ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob1.sub
+describe('bash extglob1 tests', function() {
+  var startLine = 11;
+  var tests = [
+    ['a.c', '+([[:alpha:].])', true],
+    ['a.c', '+([[:alpha:].])+([[:alpha:].])', true],
+    ['a.c', '*([[:alpha:].])', true],
+    ['a.c', '*([[:alpha:].])*([[:alpha:].])', true],
+    ['a.c', '?([[:alpha:].])?([[:alpha:].])?([[:alpha:].])', true],
+    ['a.c', '@([[:alpha:].])@([[:alpha:].])@([[:alpha:].])', true],
+    ['.', '!([[:alpha:].])', false],
+    ['.', '?([[:alpha:].])', true],
+    ['.', '@([[:alpha:].])', true],
+    ['.', '*([[:alpha:].])', true],
+    ['.', '+([[:alpha:].])', true]
+  ];
+
+  tests.forEach(function(test, i) {
+    if (!Array.isArray(test)) return;
+    var fixture = test[0];
+    var pattern = test[1];
+    var expected = test[2];
+    var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it((startLine + i) + ' ' + msg, function() {
+      assert.equal(match.isMatch(fixture, pattern), expected, msg);
+    });
+  });
+});
diff --git a/test/bash.extglob1a.js b/test/bash.extglob1a.js
new file mode 100644
index 0000000..fd87cde
--- /dev/null
+++ b/test/bash.extglob1a.js
@@ -0,0 +1,40 @@
+'use strict';
+
+require('mocha');
+var match = require('./support/match');
+var assert = require('assert');
+
+// ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob1a.sub
+describe('bash extglob1a tests', function() {
+  var startLine = 11;
+  var tests = [
+    ['a', 'a*!(x)', true],
+    ['ab', 'a*!(x)', true],
+    ['ba', 'a*!(x)', false],
+    ['ax', 'a*!(x)', true],
+    ['a', 'a!(x)', true],
+    ['ab', 'a!(x)', true],
+    ['ba', 'a!(x)', false],
+    ['ax', 'a!(x)', false],
+    ['a', 'a*?(x)', true],
+    ['ab', 'a*?(x)', true],
+    ['ba', 'a*?(x)', false],
+    ['ax', 'a*?(x)', true],
+    ['a', 'a?(x)', true],
+    ['ab', 'a?(x)', false],
+    ['ba', 'a?(x)', false],
+    ['ax', 'a?(x)', true]
+  ];
+
+  tests.forEach(function(test, i) {
+    if (!Array.isArray(test)) return;
+    var fixture = test[0];
+    var pattern = test[1];
+    var expected = test[2];
+    var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it((startLine + i) + ' ' + msg, function() {
+      assert.equal(match.isMatch(fixture, pattern), expected, msg);
+    });
+  });
+});
diff --git a/test/bash.extglob2.js b/test/bash.extglob2.js
new file mode 100644
index 0000000..5b72a67
--- /dev/null
+++ b/test/bash.extglob2.js
@@ -0,0 +1,96 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var match = require('./support/match');
+
+// tests ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob2.tests
+describe('bash extglob2 tests', function() {
+  var startLine = 11;
+  var tests = [
+    [ 'fofo', '*(f*(o))', true ],
+    [ 'ffo', '*(f*(o))', true ],
+    [ 'foooofo', '*(f*(o))', true ],
+    [ 'foooofof', '*(f*(o))', true ],
+    [ 'fooofoofofooo', '*(f*(o))', true ],
+    [ 'foooofof', '*(f+(o))', false ],
+    [ 'xfoooofof', '*(f*(o))', false ],
+    [ 'foooofofx', '*(f*(o))', false ],
+    [ 'ofxoofxo', '*(*(of*(o)x)o)', true ],
+    [ 'ofooofoofofooo', '*(f*(o))', false ],
+    [ 'foooxfooxfoxfooox', '*(f*(o)x)', true ],
+    [ 'foooxfooxofoxfooox', '*(f*(o)x)', false ],
+    [ 'foooxfooxfxfooox', '*(f*(o)x)', true ],
+    [ 'ofxoofxo', '*(*(of*(o)x)o)', true ],
+    [ 'ofoooxoofxo', '*(*(of*(o)x)o)', true ],
+    [ 'ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)', true ],
+    [ 'ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)', true ],
+    [ 'ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)', false ],
+    [ 'ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)', true ],
+    [ 'aac', '*(@(a))a@(c)', true ],
+    [ 'ac', '*(@(a))a@(c)', true ],
+    [ 'c', '*(@(a))a@(c)', false ],
+    [ 'aaac', '*(@(a))a@(c)', true ],
+    [ 'baaac', '*(@(a))a@(c)', false ],
+    [ 'abcd', '?@(a|b)*@(c)d', true ],
+    [ 'abcd', '@(ab|a*@(b))*(c)d', true ],
+    [ 'acd', '@(ab|a*(b))*(c)d', true ],
+    [ 'abbcd', '@(ab|a*(b))*(c)d', true ],
+    [ 'effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ],
+    [ 'efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ],
+    [ 'egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ],
+    [ 'egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', true ],
+    [ 'egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', false ],
+    [ 'ofoofo', '*(of+(o))', true ],
+    [ 'oxfoxoxfox', '*(oxf+(ox))', true ],
+    [ 'oxfoxfox', '*(oxf+(ox))', false ],
+    [ 'ofoofo', '*(of+(o)|f)', true ],
+    'the following is supposed to match only as fo+ofo+ofo',
+    [ 'foofoofo', '@(foo|f|fo)*(f|of+(o))', true ],
+    [ 'oofooofo', '*(of|oof+(o))', true ],
+    [ 'fffooofoooooffoofffooofff', '*(*(f)*(o))', true ],
+    'the following tests backtracking in alternation matches',
+    [ 'fofoofoofofoo', '*(fo|foo)', true ],
+    'exclusion',
+    [ 'foo', '!(x)', true ],
+    [ 'foo', '!(x)*', true ],
+    [ 'foo', '!(foo)', false ],
+    [ 'foo', '!(foo)*', false ], // Bash 4.3 disagrees!
+    [ 'foobar', '!(foo)', true ],
+    [ 'foobar', '!(foo)*', false ], // Bash 4.3 disagrees!
+    [ 'moo.cow', '!(*.*).!(*.*)', false ],  // Bash 4.3 disagrees!
+    [ 'mad.moo.cow', '!(*.*).!(*.*)', false ],
+    [ 'mucca.pazza', 'mu!(*(c))?.pa!(*(z))?', false ],
+    [ 'fff', '!(f)', true ],
+    [ 'fff', '*(!(f))', true ],
+    [ 'fff', '+(!(f))', true ],
+    [ 'ooo', '!(f)', true ],
+    [ 'ooo', '*(!(f))', true ],
+    [ 'ooo', '+(!(f))', true ],
+    [ 'foo', '!(f)', true ],
+    [ 'foo', '*(!(f))', true ],
+    [ 'foo', '+(!(f))', true ],
+    [ 'f', '!(f)', false ],
+    [ 'f', '*(!(f))', false ],
+    [ 'f', '+(!(f))', false ],
+    [ 'foot', '@(!(z*)|*x)', true ],
+    [ 'zoot', '@(!(z*)|*x)', false ],
+    [ 'foox', '@(!(z*)|*x)', true ],
+    [ 'zoox', '@(!(z*)|*x)', true ],
+    [ 'foo', '*(!(foo))', false ], // Bash 4.3 disagrees!
+    [ 'foob', '!(foo)b*', false ],
+    [ 'foobb', '!(foo)b*', false ] // Bash 4.3 disagrees!
+  ];
+
+  tests.forEach(function(test, n) {
+    if (!Array.isArray(test)) return;
+    var fixture = test[0];
+    var pattern = test[1];
+    var expected = test[2];
+    var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it((startLine + n) + ' ' + msg, function() {
+      assert.equal(match.isMatch(fixture, pattern), expected, msg);
+    });
+  });
+});
diff --git a/test/bash.extglob3.js b/test/bash.extglob3.js
new file mode 100644
index 0000000..2904c98
--- /dev/null
+++ b/test/bash.extglob3.js
@@ -0,0 +1,51 @@
+'use strict';
+
+require('mocha');
+var match = require('./support/match');
+var assert = require('assert');
+
+// ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob3.tests
+describe('bash extglob3 tests', function() {
+  var startLine = 11;
+  var tests = [
+    [ 'ab/../', '@(ab|+([^/]))/..?(/)', true ],
+    [ 'ab/../', '+([^/])/..?(/)', true ],
+    [ 'ab/../', '@(ab|?b)/..?(/)', true ],
+    [ 'ab/../', '+([^/])/../', true ],
+    [ 'ab/../', '+([!/])/..?(/)', true ],
+    [ 'ab/../', '@(ab|+([!/]))/..?(/)', true ],
+    [ 'ab/../', '+([!/])/../', true ],
+    [ 'ab/../', '+([!/])/..?(/)', true ],
+    [ 'ab/../', '+([!/])/..@(/)', true ],
+    [ 'ab/../', '+(ab)/..?(/)', true ],
+    [ 'ab/../', '[!/][!/]/../', true ],
+    [ 'ab/../', '@(ab|?b)/..?(/)', true ],
+    [ 'ab/../', '[^/][^/]/../', true ],
+    [ 'ab/../', '?b/..?(/)', true ],
+    [ 'ab/../', '+(?b)/..?(/)', true ],
+    [ 'ab/../', '+(?b|?b)/..?(/)', true ],
+    [ 'ab/../', '@(?b|?b)/..?(/)', true ],
+    [ 'ab/../', '@(a?|?b)/..?(/)', true ],
+    [ 'ab/../', '?(ab)/..?(/)', true ],
+    [ 'ab/../', '?(ab|??)/..?(/)', true ],
+    [ 'ab/../', '@(??)/..?(/)', true ],
+    [ 'ab/../', '@(??|a*)/..?(/)', true ],
+    [ 'ab/../', '@(a*)/..?(/)', true ],
+    [ 'ab/../', '+(??)/..?(/)', true ],
+    [ 'ab/../', '+(??|a*)/..?(/)', true ],
+    [ 'ab/../', '+(a*)/..?(/)', true ],
+    [ 'x', '@(x)', true ]
+  ];
+
+  tests.forEach(function(test, i) {
+    if (!Array.isArray(test)) return;
+    var fixture = test[0];
+    var pattern = test[1];
+    var expected = test[2];
+    var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it((startLine + i) + ' ' + msg, function() {
+      assert.equal(match.isMatch(fixture, pattern), expected, msg);
+    });
+  });
+});
diff --git a/test/bash.js b/test/bash.js
new file mode 100644
index 0000000..8fef521
--- /dev/null
+++ b/test/bash.js
@@ -0,0 +1,290 @@
+'use strict';
+
+require('mocha');
+var match = require('./support/match');
+var assert = require('assert');
+
+describe('running extglob against minimatch tests', function() {
+  var tests = [
+    ['*(a|b[)', '*(a|b\\[)', false],
+    ['123abc', 'ab*d+(e|f)', false],
+    ['123abc', 'ab?*(e|f)', false],
+    ['a', '!(a)', false],
+    ['a', '(b)', false],
+    ['a', '??', false],
+    ['a', 'a??b', false],
+    ['a', 'b?(a|b)', false],
+    ['a.', '*.+(b|d)', false],
+    ['a.a', '!(*.[a-b]*)', false],
+    ['a.a', '!(*.a|*.b|*.c)', false],
+    ['a.a', '!(*[a-b].[a-b]*)', false],
+    ['a.a', '!*.(a|b)', false],
+    ['a.a', '!*.(a|b)*', false],
+    ['a.a', '*.!(a)', false],
+    ['a.a', '*.+(b|d)', false],
+    ['a.a.a', '!(*.[a-b]*)', false],
+    ['a.a.a', '!(*[a-b].[a-b]*)', false],
+    ['a.a.a', '!*.(a|b)', false],
+    ['a.a.a', '!*.(a|b)*', false],
+    ['a.a.a', '*.+(b|d)', false],
+    ['a.abcd', '!(*.a|*.b|*.c)', false],
+    ['a.abcd', '!(*.a|*.b|*.c)*', false],
+    ['a.abcd', '*.!(a|b|c)', false],
+    ['a.abcd', '*.!(a|b|c)*', false],
+    ['a.b', '!(*.*)', false],
+    ['a.b', '!(*.[a-b]*)', false],
+    ['a.b', '!(*[a-b].[a-b]*)', false],
+    ['a.b', '!*.(a|b)', false],
+    ['a.b', '!*.(a|b)*', false],
+    ['a.bb', '!(*.[a-b]*)', false],
+    ['a.bb', '!(*[a-b].[a-b]*)', false],
+    ['a.bb', '!*.(a|b)', false],
+    ['a.bb', '!*.(a|b)*', false],
+    ['a.ccc', '!*.(a|b)', false],
+    ['a.ccc', '!*.(a|b)*', false],
+    ['a.ccc', '*.+(b|d)', false],
+    ['a.js', '!(*.js)', false],
+    ['a.js', '*.!(js)', false],
+    ['a.js.js', '!(*.js)', false],
+    ['aa', '?', false],
+    ['aa', '@(a)b', false],
+    ['aa', 'a??b', false],
+    ['aab', '?', false],
+    ['aab', '??', false],
+    ['aab', '@(c)b', false],
+    ['ab', 'a!(@(b|B))', false],
+    ['aB', 'a!(@(b|B))', false],
+    ['ab', 'a(*b', false],
+    ['ab', 'ab**(e|f)g', false],
+    ['ab', 'ab*+(e|f)', false],
+    ['ab', 'ab*d+(e|f)', false],
+    ['ab', 'ab?*(e|f)', false],
+    ['abcdef', '(a+|b)+', false],
+    ['abcdef', 'ab**(e|f)g', false],
+    ['abcdef', 'ab?*(e|f)', false],
+    ['abcfef', '(a+|b)+', false],
+    ['abcfef', 'ab**(e|f)g', false],
+    ['abcfef', 'ab*d+(e|f)', false],
+    ['abcfefg', '(a+|b)+', false],
+    ['abcfefg', 'ab*d+(e|f)', false],
+    ['abcfefg', 'ab?*(e|f)', false],
+    ['abd', '(a+|b)+', false],
+    ['abd', 'a!(@(b|B))d', false],
+    ['abd', 'ab*d+(e|f)', false],
+    ['abef', '(a+|b)+', false],
+    ['abef', '*(a+|b)', false],
+    ['abef', 'ab**(e|f)g', false],
+    ['abef', 'ab*d+(e|f)', false],
+    ['acd', '(a+|b)+', false],
+    ['acd', 'ab*d+(e|f)', false],
+    ['acd', 'ab?*(e|f)', false],
+    ['ax', 'a?(b*)', false],
+    ['b/a', '!(b/a)', false],
+    ['baaac', '*(@(a))a@(c)', false],
+    ['bb', 'a?(a|b)', false],
+    ['c', '*(@(a))a@(c)', false],
+    ['c.a', '!(*.[a-b]*)', false],
+    ['c.a', '!*.(a|b)', false],
+    ['c.a', '!*.(a|b)*', false],
+    ['c.a', '*.!(a)', false],
+    ['c.a', '*.+(b|d)', false],
+    ['c.js', '!(*.js)', false],
+    ['c.js', '*.!(js)', false],
+    ['cow', '.!(*.*)', false],
+    ['d.a.d', '!*.(a|b)', false],
+    ['d.a.d', '!*.(a|b)*', false],
+    ['egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', false],
+    ['f', '!(f)', false],
+    ['f', '*(!(f))', false],
+    ['f', '+(!(f))', false],
+    ['f.a', '!(*.a|*.b|*.c)', false],
+    ['f.a', '*.!(a|b|c)', false],
+    ['foo', '!(foo)', false],
+    ['foo', '!(foo)*', false], // bash 4.3 disagrees
+    ['foo', '!(foo)+', false],
+    ['foo', '!(foo)b*', false],
+    ['foo', '*(!(foo))', false],
+    ['foo.js.js', '*.!(js)*', false],
+    ['foo.js.js', '*.!(js)*.!(js)', false],
+    ['foo.js.js', '*.!(js)+', false],
+    ['foob', '!(foo)b*', false],
+    ['foobar', '!(foo)*', false], // bash 4.3 disagrees
+    ['foobar', '!(foo)b*', false],
+    ['foobb', '!(foo)b*', false],
+    ['foooofof', '*(f+(o))', false],
+    ['foooofofx', '*(f*(o))', false],
+    ['foooxfooxofoxfooox', '*(f*(o)x)', false],
+    ['mad.moo.cow', '!(*.*).!(*.*)', false],
+    ['mad.moo.cow', '.!(*.*)', false],
+    ['Makefile.in', '!(*.c|*.h|Makefile.in|config*|README)', false],
+    ['moo', '.!(*.*)', false],
+    ['moo.cow', '!(*.*).!(*.*)', false], // bash 4.3 disagrees
+    ['moo.cow', '.!(*.*)', false],
+    ['mucca.pazza', 'mu!(*(c))?.pa!(*(z))?', false],
+    ['ofooofoofofooo', '*(f*(o))', false],
+    ['ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)', false],
+    ['oxfoxfox', '*(oxf+(ox))', false],
+    ['shell.c', '!(*.c|*.h|Makefile.in|config*|README)', false],
+    ['xfoooofof', '*(f*(o))', false],
+    ['zoot', '@(!(z*)|*x)', false],
+    ['zz', '(a+|b)*', false],
+    ['a', '(a)', true],
+    ['a', '*(a)', true],
+    ['a', '+(a)', true],
+    ['a', '?', true],
+    ['a', '?(a|b)', true],
+    ['a', 'a?(a|b)', true],
+    ['a', 'a?(x)', true],
+    ['a((((b', 'a(*b', true],
+    ['a((b', 'a(*b', true],
+    ['a(b', 'a(*b', true],
+    ['a.', '!(*.a|*.b|*.c)', true],
+    ['a.', '*!(.a|.b|.c)', true],
+    ['a.', '*.!(a)', true],
+    ['a.', '*.!(a|b|c)', true],
+    ['a.a', '(a|d).(a|b)*', true],
+    ['a.a', '*!(.a|.b|.c)', true],
+    ['a.a.a', '*.!(a)', true],
+    ['a.abcd', '*!(*.a|*.b|*.c)*', true],
+    ['a.abcd', '*!(.a|.b|.c)', true],
+    ['a.b', '(a|d).(a|b)*', true],
+    ['a.b', '*!(.a|.b|.c)', true],
+    ['a.b', '*.!(a)', true],
+    ['a.b', '*.+(b|d)', true],
+    ['a.bb', '(a|d).(a|b)*', true],
+    ['a.bb', '*.+(b|d)', true],
+    ['a.c', '*!(.a|.b|.c)', true],
+    ['a.c.d', '!(*.a|*.b|*.c)', true],
+    ['a.c.d', '*!(.a|.b|.c)', true],
+    ['a.c.d', '*.!(a|b|c)', true],
+    ['a.ccc', '!(*.[a-b]*)', true],
+    ['a.ccc', '!(*[a-b].[a-b]*)', true],
+    ['a.js', '*!(.js)', true],
+    ['a.js.js', '*!(.js)', true],
+    ['a.js.js', '*.!(js)', true],
+    ['a.md', '!(*.js)', true],
+    ['a.md', '*!(.js)', true],
+    ['a.md', '*.!(js)', true],
+    ['aa', '!(a)', true],
+    ['aaac', '*(@(a))a@(c)', true],
+    ['aab', 'a??b', true],
+    ['aac', '*(@(a))a@(c)', true],
+    ['ab', '!(*.*)', true],
+    ['ab', '(a+|b)+', true],
+    ['ab', 'ab**(e|f)', true],
+    ['ab]', 'a!(@(b|B))', true],
+    ['abab', 'ab**(e|f)', true],
+    ['abb', '!(*.*)', true],
+    ['abbcd', '@(ab|a*(b))*(c)d', true],
+    ['aBc', 'a!(@(b|B))', true],
+    ['abcd', '?@(a|b)*@(c)d', true],
+    ['abcd', '@(ab|a*@(b))*(c)d', true],
+    ['abcdef', '(a+|b)*', true],
+    ['abcdef', 'ab**(e|f)', true],
+    ['abcdef', 'ab*+(e|f)', true],
+    ['abcdef', 'ab*d+(e|f)', true],
+    ['abcfef', '(a+|b)*', true],
+    ['abcfef', 'ab**(e|f)', true],
+    ['abcfef', 'ab*+(e|f)', true],
+    ['abcfef', 'ab?*(e|f)', true],
+    ['abcfefg', '(a+|b)*', true],
+    ['abcfefg', 'ab**(e|f)', true],
+    ['abd', '(a+|b)*', true],
+    ['abd', 'a!(@(b|B))', true],
+    ['abd', 'ab**(e|f)', true],
+    ['abd', 'ab?*(e|f)', true],
+    ['abef', '(a+|b)*', true],
+    ['abef', 'ab**(e|f)', true],
+    ['abef', 'ab*+(e|f)', true],
+    ['abef', 'ab?*(e|f)', true],
+    ['ac', '*(@(a))a@(c)', true],
+    ['ac', 'a!(@(b|B))', true],
+    ['acd', '(a+|b)*', true],
+    ['acd', '@(ab|a*(b))*(c)d', true],
+    ['acd', 'a!(@(b|B))', true],
+    ['acd', 'a!(@(b|B))d', true],
+    ['ax', '?(a*|b)', true],
+    ['b', '(a+|b)*', true],
+    ['b/b', '!(b/a)', true],
+    ['b/c', '!(b/a)', true],
+    ['ba', 'b?(a|b)', true],
+    ['bar', '!(foo)*', true],
+    ['bar', '!(foo)b*', true],
+    ['baz', '!(foo)*', true],
+    ['baz', '!(foo)b*', true],
+    ['c.a', '!(*[a-b].[a-b]*)', true],
+    ['c.c', '*!(.a|.b|.c)', true],
+    ['c.ccc', '!(*.[a-b]*)', true],
+    ['c.ccc', '!(*[a-b].[a-b]*)', true],
+    ['c.js', '*!(.js)', true],
+    ['d.a.d', '!(*.[a-b]*)', true],
+    ['d.a.d', '!(*[a-b].[a-b]*)', true],
+    ['d.a.d', '*.!(a)', true],
+    ['d.a.d', '*.+(b|d)', true],
+    ['d.d', '!(*.a|*.b|*.c)', true],
+    ['d.d', '*!(.a|.b|.c)', true],
+    ['d.d', '*.!(a|b|c)', true],
+    ['d.js.d', '!(*.js)', true],
+    ['d.js.d', '*!(.js)', true],
+    ['d.js.d', '*.!(js)', true],
+    ['e.e', '!(*.a|*.b|*.c)', true],
+    ['e.e', '*!(.a|.b|.c)', true],
+    ['e.e', '*.!(a|b|c)', true],
+    ['effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true],
+    ['efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true],
+    ['egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', true],
+    ['egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', true],
+    ['f.a', '*!(.a|.b|.c)', true],
+    ['f.f', '!(*.a|*.b|*.c)', true],
+    ['f.f', '*!(.a|.b|.c)', true],
+    ['f.f', '*.!(a|b|c)', true],
+    ['fff', '!(f)', true],
+    ['fff', '*(!(f))', true],
+    ['fff', '+(!(f))', true],
+    ['fffooofoooooffoofffooofff', '*(*(f)*(o))', true],
+    ['ffo', '*(f*(o))', true],
+    ['fofo', '*(f*(o))', true],
+    ['fofoofoofofoo', '*(fo|foo)', true],
+    ['foo', '!(f)', true],
+    ['foo', '!(x)', true],
+    ['foo', '!(x)*', true],
+    ['foo', '*(!(f))', true],
+    ['foo', '+(!(f))', true],
+    ['foo.js.js', '*.!(js)', true],
+    ['foobar', '!(foo)', true],
+    ['foofoofo', '@(foo|f|fo)*(f|of+(o))', true],
+    ['fooofoofofooo', '*(f*(o))', true],
+    ['foooofo', '*(f*(o))', true],
+    ['foooofof', '*(f*(o))', true],
+    ['foooxfooxfoxfooox', '*(f*(o)x)', true],
+    ['foooxfooxfxfooox', '*(f*(o)x)', true],
+    ['foot', '@(!(z*)|*x)', true],
+    ['foox', '@(!(z*)|*x)', true],
+    ['Makefile', '!(*.c|*.h|Makefile.in|config*|README)', true],
+    ['ofoofo', '*(of+(o))', true],
+    ['ofoofo', '*(of+(o)|f)', true],
+    ['ofoooxoofxo', '*(*(of*(o)x)o)', true],
+    ['ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)', true],
+    ['ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)', true],
+    ['ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)', true],
+    ['ofxoofxo', '*(*(of*(o)x)o)', true],
+    ['oofooofo', '*(of|oof+(o))', true],
+    ['ooo', '!(f)', true],
+    ['ooo', '*(!(f))', true],
+    ['ooo', '+(!(f))', true],
+    ['oxfoxoxfox', '*(oxf+(ox))', true],
+    ['parse.y', '!(*.c|*.h|Makefile.in|config*|README)', true],
+    ['zoox', '@(!(z*)|*x)', true]
+  ];
+
+  tests.forEach(function(test) {
+    var fixture = test[0];
+    var pattern = test[1];
+    var expected = test[2];
+    var msg = 'should ' + (expected ? '' : 'not ') + 'match ' + pattern;
+
+    it(msg, function() {
+      assert.equal(match.isMatch(fixture, pattern), expected, msg);
+    });
+  });
+});
diff --git a/test/errors.js b/test/errors.js
new file mode 100644
index 0000000..2fe1c70
--- /dev/null
+++ b/test/errors.js
@@ -0,0 +1,66 @@
+'use strict';
+
+var assert = require('assert');
+var extglob = require('..');
+
+describe('errors', function() {
+  it('should throw an error when extglob() receives an invalid pattern', function(cb) {
+    try {
+      assert(extglob());
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected pattern to be a string');
+    }
+    cb();
+  });
+
+  it('should throw an error when extglob.isMatch() receives an invalid pattern', function(cb) {
+    try {
+      assert(extglob.isMatch());
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected pattern to be a string');
+    }
+    cb();
+  });
+
+  it('should throw an error when extglob.makeRe() receives an invalid pattern', function(cb) {
+    try {
+      assert(extglob.makeRe());
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected pattern to be a string');
+    }
+    cb();
+  });
+
+  it('should throw an error when extglob.create() receives an invalid pattern', function(cb) {
+    try {
+      assert(extglob.create());
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected pattern to be a string');
+    }
+    cb();
+  });
+
+  it('should throw an error when extglob.isMatch() receives an invalid string', function(cb) {
+    try {
+      assert(extglob.isMatch(null, '*'));
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected a string');
+    }
+    cb();
+  });
+
+  it('should throw an error when extglob.match() receives an invalid pattern', function(cb) {
+    try {
+      assert(extglob.match());
+      cb(new Error('expected an error'));
+    } catch (err) {
+      assert.equal(err.message, 'expected pattern to be a string');
+    }
+    cb();
+  });
+});
diff --git a/test/fixtures/posix.txt b/test/fixtures/posix.txt
new file mode 100644
index 0000000..ee0d5ec
--- /dev/null
+++ b/test/fixtures/posix.txt
@@ -0,0 +1,233 @@
+# A test suite for the POSIX.2 (BRE) pattern matching code
+LC_ALL=C
+LANG=C
+
+# First, test POSIX.2 character classes
+
+case e in
+[[:xdigit:]]) echo ok 1;;
+esac
+
+case a in
+[[:alpha:]123]) echo ok 2;;
+esac
+
+case 1 in
+[[:alpha:]123]) echo ok 3;;
+esac
+
+case 9 in
+[![:alpha:]]) echo ok 4;;
+esac
+
+# invalid character class expressions are just characters to be matched
+case a in
+[:al:])   echo ok 5;;
+esac
+
+case a in
+[[:al:])  echo ok 6;;
+esac
+
+case '!' in
+[abc[:punct:][0-9]) echo ok 7;;
+esac
+
+# let's try to match the start of a valid sh identifier
+case 'PATH' in
+[_[:alpha:]]*)  echo ok 8;;
+esac
+
+# let's try to match the first two characters of a valid sh identifier
+case PATH in
+[_[:alpha:]][_[:alnum:]]*)  echo ok 9;;
+esac
+
+# is ^C a cntrl character?
+case $'\003' in
+[[:cntrl:]])  echo ok 10;;
+esac
+
+# how about A?
+case A in
+[[:cntrl:]])  echo oops -- cntrl ;;
+*)    echo ok 11;;
+esac
+
+case 9 in
+[[:digit:]])  echo ok 12;;
+esac
+
+case X in
+[[:digit:]])  echo oops -- digit;;
+*)    echo ok 13;;
+esac
+
+case $'\033' in
+[[:graph:]])  echo oops -- graph;;
+*)    echo ok 14;;
+esac
+
+case $'\040' in
+[[:graph:]])  echo oops -- graph 2;;
+*)    echo ok 15;;
+esac
+
+case ' ' in
+[[:graph:]])  echo oops -- graph 3;;
+*)    echo ok 16;;
+esac
+
+case 'aB' in
+[[:lower:]][[:upper:]]) echo ok 17;;
+esac
+
+case $'\040' in
+[[:print:]])  echo ok 18;;
+*)    echo oops -- print;;
+esac
+
+case PS3 in
+[_[:alpha:]][_[:alnum:]][_[:alnum:]]*)  echo ok 19;;
+esac
+
+case a in
+[[:alpha:][:digit:]]) echo ok 20;;
+*)      echo oops - skip brackpat ;;
+esac
+
+case a in
+[[:alpha:]\]) echo oops -- dangling backslash in brackpat ;;
+*)    echo ok 21 ;;
+esac
+
+# what's a newline?  is it a blank? a space?
+case $'\n' in
+[[:blank:]])  echo ok -- blank ;;
+[[:space:]])  echo ok -- space ;;
+*)    echo oops newline ;;
+esac
+
+# OK, what's a tab?  is it a blank? a space?
+case $'\t' in
+[[:blank:]])  echo ok -- blank ;;
+[[:space:]])  echo ok -- space ;;
+*)    echo oops newline ;;
+esac
+
+# let's check out characters in the ASCII range
+case $'\377' in
+[[:ascii:]])  echo oops -- ascii\?;;
+esac
+
+case 9 in
+[1[:alpha:]123]) echo oops 1;;
+esac
+
+# however, an unterminated brace expression containing a valid char class
+# that matches had better fail
+case a in
+[[:alpha:]) echo oops 2;;
+esac
+
+case $'\b' in
+[[:graph:]])  echo oops 3;;
+esac
+
+case $'\b' in
+[[:print:]])  echo oops 4;;
+esac
+
+case $' ' in
+[[:punct:]])  echo oops 5;;
+esac
+
+# Next, test POSIX.2 collating symbols
+
+case 'a' in
+[[.a.]])  echo ok 1;;
+esac
+
+case '-' in
+[[.hyphen.]-9]) echo ok 2;;
+esac
+
+case 'p' in
+[[.a.]-[.z.]])  echo ok 3;;
+esac
+
+case '-' in
+[[.-.]])  echo ok 4;;
+esac
+
+case ' ' in
+[[.space.]])  echo ok 5;;
+esac
+
+case ' ' in
+[[.grave-accent.]]) echo oops - grave;;
+*)    echo ok 6;;
+esac
+
+case '4' in
+[[.-.]-9])  echo ok 7;;
+esac
+
+# an invalid collating symbol cannot be the first part of a range
+case 'c' in
+[[.yyz.]-[.z.]])  echo oops - yyz;;
+*)    echo ok 8;;
+esac
+
+case 'c' in
+[[.yyz.][.a.]-z])   echo ok 9;;
+esac
+
+# but when not part of a range is not an error
+case 'c' in
+[[.yyz.][.a.]-[.z.]])   echo ok 10 ;;
+esac
+
+case 'p' in
+[[.a.]-[.Z.]])    echo oops -- bad range ;;
+*)      echo ok 11;;
+esac
+
+case p in
+[[.a.]-[.zz.]p])  echo ok 12;;
+*)      echo oops -- bad range 2;;
+esac
+
+case p in
+[[.aa.]-[.z.]p])  echo ok 13;;
+*)      echo oops -- bad range 3;;
+esac
+
+case c in
+[[.yyz.]cde])   echo ok 14;;
+esac
+
+case abc in
+[[.cb.]a-Za]*)    echo ok 15;;
+esac
+
+case $'\t' in
+[[.space.][.tab.][.newline.]])  echo ok 16;;
+esac
+
+# and finally, test POSIX.2 equivalence classes
+
+case "abc" in
+[[:alpha:]][[=b=]][[:ascii:]])  echo ok 1;;
+esac
+
+case "abc" in
+[[:alpha:]][[=B=]][[:ascii:]])  echo oops -- =B=;;
+*)  echo ok 2 ;;
+esac
+
+case a in
+[[=b=])   echo oops;; # an incomplete equiv class is just a string
+*)    echo ok 3;;
+esac
+
diff --git a/test/negations.js b/test/negations.js
new file mode 100644
index 0000000..d5e4979
--- /dev/null
+++ b/test/negations.js
@@ -0,0 +1,136 @@
+'use strict';
+
+/**
+ * minimatch "tricky negations test"
+ */
+
+module.exports = {
+  'bar.min.js': {
+    '*.!(js|css)': true,
+    '!*.+(js|css)': false,
+    '*.+(js|css)': true
+  },
+
+  'a-integration-test.js': {
+    '*.!(j)': true,
+    '*.!(js)': false,
+    '!(*-integration-test.js)': false,
+    '*-!(integration-)test.js': true,
+    '*-!(integration)-test.js': false,
+    '*!(-integration)-test.js': true,
+    '*!(-integration-)test.js': true,
+    '*!(integration)-test.js': true,
+    '*!(integration-test).js': true,
+    '*-!(integration-test).js': true,
+    '*-!(integration-test.js)': true,
+    '*-!(integra)tion-test.js': false,
+    '*-integr!(ation)-test.js': false,
+    '*-integr!(ation-t)est.js': false,
+    '*-i!(ntegration-)test.js': false,
+    '*i!(ntegration-)test.js': true,
+    '*te!(gration-te)st.js': true,
+    '*-!(integration)?test.js': false,
+    '*?!(integration)?test.js': true
+  },
+
+  'foo-integration-test.js': {
+    'foo-integration-test.js': true,
+    '!(*-integration-test.js)': false
+  },
+
+  'foo.jszzz.js': {
+    '*.!(js).js': true
+  },
+
+  'asd.jss': {
+    '*.!(js)': true
+  },
+
+  'asd.jss.xyz': {
+    '*.!(js).!(xy)': true
+  },
+
+  'asd.jss.xy': {
+    '*.!(js).!(xy)': false
+  },
+
+  'asd.js.xyz': {
+    '*.!(js)*.!(xyz)*': false,
+    '*.!(js)*.!(xyz)': false,
+    '*.!(js).!(xyz)': false,
+    '*.!(js)*.!(xy)*': false,
+    '*.!(js)*.!(xy)': false,
+    '*.!(js).!(xy)*': false,
+    '*.!(js).!(xy)': true
+  },
+
+  'asd.js.xy': {
+    '*.!(js).!(xy)': false
+  },
+
+  'asd.sjs.zxy': {
+    '*.!(js).!(xy)': true
+  },
+
+  'asd..xyz': {
+    '*.!(js).!(xy)': true
+  },
+
+  'asd..xy': {
+    '*.!(js).!(xy)': false,
+    '*.!(js|x).!(xy)': false
+  },
+
+  'foo.js.js': {
+    '*!(js)': true,
+    '*!(.js)': true,
+    '*!(.js.js)': true,
+    '*!(.js.js)*': true,
+    '*(.js.js)': false,
+    '**(.js.js)': true,
+    '*(!(.js.js))': true,
+    '*.!(js)*.!(js)': false,
+    '*.!(js)+': false,
+    '!(*(.js.js))': true,
+    '*.!(js)': true,
+    '*.!(js)*': false,     // Bash 4.3 disagrees!
+    '*.!(js)*.js': false  // Bash 4.3 disagrees!
+  },
+
+  'a/foo.js.js': {
+    '*/**(.*)': true,
+    '*/**(.*.*)': true,
+    'a/**(.*.*)': true,
+    '*/**(.js.js)': true,
+    'a/f*(!(.js.js))': true,
+    'a/!(*(.*))': true,
+    'a/!(+(.*))': true,
+    'a/!(*(.*.*))': true,
+    '*/!(*(.*.*))': true,
+    'a/!(*(.js.js))': true
+  },
+
+  'testjson.json': {
+    '*(*.json|!(*.js))': true,
+    '+(*.json|!(*.js))': true,
+    '@(*.json|!(*.js))': true,
+    '?(*.json|!(*.js))': true
+  },
+
+  'foojs.js': {
+    '*(*.json|!(*.js))': false, // Bash 4.3 disagrees!
+    '*(*.json|!(*.js))*': true,
+    '+(*.json|!(*.js))': false, // Bash 4.3 disagrees!
+    '@(*.json|!(*.js))': false,
+    '?(*.json|!(*.js))': false
+  },
+
+  'other.bar': {
+    '*(*.json|!(*.js))': true,
+    '*(*.json|!(*.js))*': true,
+    '!(*(*.json|!(*.js)))*': false,
+    '+(*.json|!(*.js))': true,
+    '@(*.json|!(*.js))': true,
+    '?(*.json|!(*.js))': true
+  }
+};
diff --git a/test/options.js b/test/options.js
new file mode 100644
index 0000000..fe1e1ea
--- /dev/null
+++ b/test/options.js
@@ -0,0 +1,41 @@
+'use strict';
+
+var assert = require('assert');
+var matcher = require('./support/match');
+var extglob = require('..');
+
+describe('options', function() {
+  describe('options.nonull', function() {
+    it('should return the pattern when no matches are found', function() {
+      matcher.match(['ax'], 'a?(b*)', []);
+      matcher.match(['ax'], 'a?(b*)', ['a?(b*)'], {nonull: true});
+      matcher.match(['az'], 'a?(b*)', ['a?(b*)'], {nonull: true});
+      matcher.match(['ag'], 'a?(b*)', ['a?(b*)'], {nonull: true});
+    });
+  });
+
+  describe('options.failglob', function() {
+    it('should throw an error when no matches are found', function(cb) {
+      try {
+        extglob.match(['ax'], 'a?(b*)', {failglob: true});
+        return cb(new Error('expected an error'));
+      } catch (err) {
+        assert(/no matches/.test(err.message));
+      }
+      cb();
+    });
+  });
+
+  describe('options.strict', function() {
+    it('should throw an error when an opening brace is missing', function(cb) {
+      assert(!extglob.isMatch('foo', 'a)'));
+      try {
+        assert(!extglob.isMatch('foo', 'a)', {strict: true}));
+        return cb(new Error('expected an error'));
+      } catch (err) {
+        assert(/missing/.test(err.message));
+      }
+      cb();
+    });
+  });
+});
diff --git a/test/reference.js b/test/reference.js
new file mode 100644
index 0000000..3bd995e
--- /dev/null
+++ b/test/reference.js
@@ -0,0 +1,25 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var forOwn = require('for-own');
+var negations = require('./negations');
+var matcher = require('./support/matcher');
+
+describe('running extglob against minimatch tests', function() {
+  forOwn(negations, function(val, fixture) {
+    if (fixture !== 'asd.js.xyz') return;
+
+    describe('"' + fixture + '"', function() {
+      forOwn(val, function(expected, pattern) {
+        var exp = expected === false ? ' not' : '';
+
+        it('should' + exp + ' match "' + pattern + '"', function() {
+          var actual = matcher.isMatch(fixture, pattern);
+          if (actual === null) return;
+          assert.equal(actual, expected, pattern);
+        });
+      });
+    });
+  });
+});
diff --git a/test/support/compare.js b/test/support/compare.js
new file mode 100644
index 0000000..8a4aef0
--- /dev/null
+++ b/test/support/compare.js
@@ -0,0 +1,5 @@
+module.exports = function(a, b) {
+  a = a.toLowerCase();
+  b = b.toLowerCase();
+  return a > b ? 1 : a < b ? -1 : 0;
+};
diff --git a/test/support/match.js b/test/support/match.js
new file mode 100644
index 0000000..7c0bdb3
--- /dev/null
+++ b/test/support/match.js
@@ -0,0 +1,60 @@
+'use strict';
+
+var assert = require('assert');
+var matcher = require('./matcher');
+var compare = require('./compare');
+var utils = require('./utils');
+
+module.exports = function(fixtures, pattern, expected, options, msg) {
+  if (!Array.isArray(expected)) {
+    var tmp = expected;
+    expected = options;
+    options = tmp;
+  }
+
+  if (typeof options === 'string') {
+    msg = options;
+    options = {};
+  }
+
+  msg = msg ? (pattern + ' ' + msg) : pattern;
+
+  var actual = matcher.match(utils.arrayify(fixtures), pattern, options);
+  expected.sort(compare);
+  actual.sort(compare);
+
+  assert.deepEqual(actual, expected, msg);
+};
+
+module.exports.match = function(fixtures, pattern, expected, options, msg) {
+  if (!Array.isArray(expected)) {
+    var tmp = expected;
+    expected = options;
+    options = tmp;
+  }
+
+  if (typeof options === 'string') {
+    msg = options;
+    options = {};
+  }
+
+  msg = msg ? (pattern + ' ' + msg) : pattern;
+
+  var actual = matcher.match(utils.arrayify(fixtures), pattern, options);
+  expected.sort(compare);
+  actual.sort(compare);
+
+  assert.deepEqual(actual, expected, msg);
+};
+
+module.exports.isMatch = function(fixture, pattern, options) {
+  return matcher.isMatch.apply(null, arguments);
+};
+
+module.exports.contains = function(fixture, pattern, options) {
+  return matcher.contains.apply(null, arguments);
+};
+
+module.exports.makeRe = function(pattern, options) {
+  return matcher.makeRe.apply(null, arguments);
+};
diff --git a/test/support/matcher.js b/test/support/matcher.js
new file mode 100644
index 0000000..460ca68
--- /dev/null
+++ b/test/support/matcher.js
@@ -0,0 +1,78 @@
+'use strict';
+
+var argv = require('yargs-parser')(process.argv.slice(2));
+var mm = require('multimatch');
+var minimatch = require('minimatch');
+var bash = require('./try-bash');
+var utils = require('./utils');
+var extglob = require('../..');
+
+// use multimatch for the array/array scenario
+function mi() {
+  return mm.apply(null, arguments);
+}
+
+// label for debugging
+mm.multimatch = true;
+mi.minimatch = true;
+extglob.extglob = true;
+bash.bash = true;
+
+/**
+ * Decorate methods onto multimatch for parity with nanomatch
+ */
+
+mm.isMatch = function(files, patterns, options) {
+  return mm(utils.arrayify(files), patterns, options).length > 0;
+};
+
+mm.contains = function(files, patterns, options) {
+  return mm.isMatch(files, patterns, options);
+};
+
+mm.match = function(files, patterns, options) {
+  return mm(utils.arrayify(files), patterns, options);
+};
+
+mm.makeRe = function(pattern, options) {
+  return mi.makeRe(pattern, options);
+};
+
+/**
+ * Decorate methods onto minimatch for parity with nanomatch
+ */
+
+mi.isMatch = function(file, pattern, options) {
+  return minimatch(file, pattern, options);
+};
+
+mi.contains = function(files, patterns, options) {
+  return mi.isMatch(files, patterns, options);
+};
+
+mi.match = function(files, pattern, options) {
+  return minimatch.match(utils.arrayify(files), pattern, options);
+};
+
+mi.makeRe = function(pattern, options) {
+  return minimatch.makeRe(pattern, options);
+};
+
+/**
+ * Detect matcher based on argv, with nanomatch as default
+ */
+
+var matcher = argv.mm ? mm : (argv.mi ? mi : extglob);
+if (argv.bash) {
+  matcher = bash;
+}
+
+/**
+ * Expose matcher
+ */
+
+module.exports = matcher;
+module.exports.bash = bash;
+module.exports.extglob = extglob;
+module.exports.mm = mm;
+module.exports.mi = mi;
diff --git a/test/support/parse.js b/test/support/parse.js
new file mode 100644
index 0000000..14221cf
--- /dev/null
+++ b/test/support/parse.js
@@ -0,0 +1,46 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var mm = require('micromatch');
+
+function parseFiles(pattern, options) {
+  var opts = Object.assign({cwd: path.join(__dirname, '../fixtures')}, options);
+  var cwd = opts.cwd;
+
+  var files = mm(fs.readdirSync(cwd), pattern);
+  var tests = {};
+
+  for (var i = 0; i < files.length; i++) {
+    var file = files[i];
+    var name = path.basename(file, path.extname(file));
+    tests[name] = parse(path.join(cwd, file));
+  }
+  return tests;
+}
+
+function parse(fp) {
+  var str = fs.readFileSync(fp, 'utf8');
+  var lines = str.split('\n');
+  var len = lines.length;
+  var idx = -1;
+  var tests = [];
+
+  while (++idx < len) {
+    var line = lines[idx].trim();
+
+    if (!line) continue;
+    if (/^#\s\w/.test(line)) {
+      tests.push(line.replace(/^[#\s]+/, '').toLowerCase());
+      continue;
+    }
+    if (!/^[tf] /.test(line)) continue;
+
+    var segs = line.split(/\s+/).filter(Boolean);
+    if (segs.length !== 3) continue;
+    tests.push([segs[1], segs[2], segs[0] === 't']);
+  }
+  return tests.filter(Boolean);
+}
+
+module.exports = parseFiles;
diff --git a/test/support/try-bash.js b/test/support/try-bash.js
new file mode 100644
index 0000000..1534433
--- /dev/null
+++ b/test/support/try-bash.js
@@ -0,0 +1,31 @@
+'use strict';
+
+var bash = require('bash-match');
+var isWindows = require('is-windows');
+
+function matcher() {
+  if (isWindows()) return;
+  return bash.apply(null, arguments);
+}
+
+matcher.match = function(fixtures, pattern) {
+  if (isWindows()) return;
+  try {
+    return bash.match(fixtures, pattern);
+  } catch (err) {}
+  return null;
+};
+
+matcher.isMatch = function(fixtures, pattern) {
+  if (isWindows()) return;
+  try {
+    return bash.isMatch(fixtures, pattern);
+  } catch (err) {}
+  return null;
+};
+
+/**
+ * Expose `matcher`
+ */
+
+module.exports = matcher;
diff --git a/test/support/utils.js b/test/support/utils.js
new file mode 100644
index 0000000..30d2e9c
--- /dev/null
+++ b/test/support/utils.js
@@ -0,0 +1,11 @@
+'use strict';
+
+exports.arrayify = function(val) {
+  return val ? (Array.isArray(val) ? val : [val]) : [];
+};
+
+exports.alphaSort = function(a, b) {
+  a = a.toLowerCase();
+  b = b.toLowerCase();
+  return a > b ? 1 : a < b ? -1 : 0;
+};
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..4d87eaf
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,515 @@
+'use strict';
+
+var assert = require('assert');
+var match = require('./support/match');
+var extglob = require('..');
+
+/**
+ * These tests were converted directly from bash 4.3 and 4.4 unit tests.
+ */
+
+describe('extglobs', function() {
+  it('should export a function', function() {
+    assert.equal(typeof extglob, 'function');
+  });
+
+  it.skip('failing unit test from bash', function() {
+    match(['moo.cow'], '!(*.*).!(*.*)', ['moo.cow']);
+    match(['foo.js.js'], '*.!(js)*', ['foo.js.js']);
+  });
+
+  it('should throw on imbalanced sets when `options.strictErrors` is true', function() {
+    assert.throws(function() {
+      match.isMatch('a((b', 'a(b', {strictErrors: true});
+    }, 'row:1 col:2 missing opening parens: "a(b"');
+
+    assert.throws(function() {
+      match.isMatch('a((b', 'a(*b', {strictErrors: true});
+    }, 'row:1 col:2 missing opening parens: "a(*b"');
+  });
+
+  // from minimatch tests
+  it('should match extglobs ending with statechar', function() {
+    assert(!match.isMatch('ax', 'a?(b*)'));
+    assert(match.isMatch('ax', '?(a*|b)'));
+  });
+
+  it('should not choke on non-extglobs', function() {
+    match(['c/z/v'], 'c/z/v', ['c/z/v']);
+  });
+
+  it('should support star (`*`) extglobs', function() {
+    match(['cz', 'abz', 'az'], 'a*(z)', ['az']);
+    match(['cz', 'abz', 'az'], 'a**(z)', ['az', 'abz']);
+    match(['c/z/v', 'z', 'zf', 'fz'], '*(z)', ['z']);
+    match(['c/z/v', 'c/a/v'], 'c/*(z)/v', ['c/z/v']);
+    match(['a.js.js', 'a.md.js'], '*.*(js).js', ['a.js.js']);
+  });
+
+  it('should support negation (`!`) extglobs', function() {
+    match(['c/z/v', 'c/a/v'], 'c/!(z)/v', ['c/a/v']);
+    match(['c/z/v', 'c/a/v'], 'c/!(z)/v', ['c/a/v']);
+    match(['cz', 'abz', 'az'], 'a!(z)', ['abz']);
+    match(['cz', 'abz', 'az'], 'a*!(z)', ['az', 'abz']);
+    match(['c/a/v'], 'c/!(z)/v', ['c/a/v']);
+    match(['a/z', 'a/b'], 'a/!(z)', ['a/b']);
+
+    var f1 = ['a/a', 'a/b', 'a/c', 'b/a', 'b/b', 'b/c'];
+    match(f1, '*(b/a)', ['b/a']);
+    match(f1, '!(b/a)', [], {star: '[^/]*?'});
+    match(f1, '!((b/a))', [], {star: '[^/]*?'});
+    match(f1, '!((?:b/a))', [], {star: '[^/]*?'});
+    match(f1, '!(b/(a))', [], {star: '[^/]*?'});
+
+    match(f1, '!(b/a)', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']);
+    match(f1, '!((b/a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']);
+    match(f1, '!((?:b/a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']);
+    match(f1, '!(b/(a))', ['a/a', 'a/b', 'a/c', 'b/b', 'b/c']);
+
+    var f2 = ['a', 'b', 'aa', 'ab', 'bb', 'ac', 'aaa', 'aab', 'abb', 'ccc'];
+    match(f2, '!(a)', ['aa', 'aaa', 'aab', 'ab', 'abb', 'ac', 'b', 'bb', 'ccc']);
+    match(f2, '!(a)*', ['b', 'bb', 'ccc']);
+    match(f2, 'a!(b)*', ['a', 'aa', 'aaa', 'aab', 'ac']);
+  });
+
+  it('should support plus (`+`) extglobs', function() {
+    match(['cz', 'abz', 'az'], 'a+(z)', ['az']);
+    match(['c/z/v', 'z', 'zf', 'fz'], '+(z)', ['z']);
+    match(['c/z/v', 'c/a/v'], 'c/+(z)/v', ['c/z/v']);
+    match(['az', 'bz', 'axz'], 'a+(z)', ['az']);
+  });
+
+  it('should support qmark (`?`) extglobs', function() {
+    match(['c/z/v', 'z', 'zf', 'fz'], '?(z)', ['z']);
+    match(['cz', 'abz', 'az'], 'a?(z)', ['az']);
+  });
+
+  it('should support ampersand (`@`) extglobs', function() {
+    match(['c/z/v', 'c/a/v'], 'c/@(z)/v', ['c/z/v']);
+    match(['cz', 'abz', 'az'], 'a*@(z)', ['az', 'abz']);
+    match(['cz', 'abz', 'az'], 'a@(z)', ['az']);
+  });
+
+  it('should support qmark matching', function() {
+    var arr = ['a', 'aa', 'ab', 'aaa', 'abcdefg'];
+    match(arr, '?', ['a']);
+    match(arr, '??', ['aa', 'ab']);
+    match(arr, '???', ['aaa']);
+  });
+
+  it('should match exactly one of the given pattern:', function() {
+    var arr = ['aa.aa', 'a.bb', 'a.aa.a', 'cc.a', 'a.a', 'c.a', 'dd.aa.d', 'b.a'];
+    match(arr, '(b|a).(a)', ['a.a', 'b.a']);
+    match(arr, '@(b|a).@(a)', ['a.a', 'b.a']);
+  });
+
+  it('stuff from korn\'s book', function() {
+    assert(!match.isMatch('para', 'para+([0-9])'));
+    assert(!match.isMatch('para381', 'para?([345]|99)1'));
+    assert(!match.isMatch('paragraph', 'para*([0-9])'));
+    assert(!match.isMatch('paragraph', 'para*([0-9])'));
+    assert(!match.isMatch('paramour', 'para@(chute|graph)'));
+    assert(match.isMatch('para', 'para*([0-9])'));
+    assert(match.isMatch('para.38', 'para!(*.[00-09])'));
+    assert(match.isMatch('para.graph', 'para!(*.[0-9])'));
+    assert(match.isMatch('para13829383746592', 'para*([0-9])'));
+    assert(match.isMatch('para39', 'para!(*.[0-9])'));
+    assert(match.isMatch('para987346523', 'para+([0-9])'));
+    assert(match.isMatch('para991', 'para?([345]|99)1'));
+    assert(match.isMatch('paragraph', 'para!(*.[0-9])'));
+    assert(match.isMatch('paragraph', 'para@(chute|graph)'));
+  });
+
+  it('tests derived from those in rosenblatt\'s korn shell book', function() {
+    match(['', '137577991', '2468'], '*(0|1|3|5|7|9)', ['', '137577991']);
+    match(['file.c', 'file.C', 'file.cc', 'file.ccc'], '*.c?(c)', ['file.c', 'file.cc']);
+    match(['parse.y', 'shell.c', 'Makefile', 'Makefile.in'], '!(*.c|*.h|Makefile.in|config*|README)', ['parse.y', 'Makefile']);
+    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']);
+  });
+
+  it('tests derived from the pd-ksh test suite', function() {
+    match(['abcx', 'abcz', 'bbc'], '!([[*])*', ['abcx', 'abcz', 'bbc']);
+    match(['abcx', 'abcz', 'bbc'], '+(a|b\\[)*', ['abcx', 'abcz']);
+    match(['abd', 'acd'], 'a+(b|c)d', ['abd', 'acd']);
+    match(['abd', 'acd', 'ac', 'ab'], 'a!(@(b|B))', ['acd', 'abd', 'ac']);
+    match(['abd', 'acd'], 'a!(@(b|B))d', ['acd']);
+    match(['abd', 'acd'], 'a[b*(foo|bar)]d', ['abd']);
+    match(['abcx', 'abcz', 'bbc', 'aaz', 'aaaz'], '[a*(]*z', ['aaz', 'aaaz', 'abcz']);
+  });
+
+  it('simple kleene star tests', function() {
+    assert(!match.isMatch('foo', '*(a|b\\[)'));
+    assert(match.isMatch('foo', '*(a|b\\[)|f*'));
+  });
+
+  it('this doesn\'t work in bash either (per bash extglob.tests notes)', function() {
+    assert(!match.isMatch('*(a|b[)', '*(a|b\\[)'));
+    assert(match.isMatch('*(a|b[)', '\\*\\(a\\|b\\[\\)'));
+  });
+
+  it('should support multiple extglobs:', function() {
+    var arr = ['a.a', 'a.b', 'a.c', 'a.c.d', 'c.c', 'a.', 'd.d', 'e.e', 'f.f', 'a.abcd'];
+    match(arr, '*.(a|b|@(ab|a*@(b))*(c)d)', ['a.a', 'a.b', 'a.abcd']);
+    match(arr, '!(*.a|*.b|*.c)', ['a.', 'a.c.d', 'd.d', 'e.e', 'f.f']);
+    match(arr, '!(*.[^a-c])', ['a.a', 'a.b', 'a.c', 'c.c', 'a.', 'a.abcd']);
+    match(arr, '!(*.[a-c])', ['a.', 'a.c.d', 'a.abcd', 'd.d', 'e.e', 'f.f']);
+    match(arr, '!(*.[a-c]*)', ['a.', 'a.c.d', 'd.d', 'e.e', 'f.f']);
+    match(arr, '!(*.[a-c])*', ['a.', 'd.d', 'e.e', 'f.f']);
+    match(arr, '*!(.a|.b|.c)', arr);
+    match(arr, '*!(.a|.b|.c)*', arr);
+    match(arr, '*.!(a|b|c)', ['a.c.d', 'a.', 'd.d', 'e.e', 'f.f']);
+    match(arr, '*.!(a|b|c)*', ['a.c.d', 'a.', 'd.d', 'e.e', 'f.f']);
+  });
+
+  it('should correctly match empty parens', function() {
+    match(['def', 'ef'], '()ef', ['ef']);
+  });
+
+  it('should match escaped parens', function() {
+    var arr = ['a(b', 'a\\(b', 'a((b', 'a((((b', 'ab'];
+    match(arr, 'a(b', ['a(b']);
+    match(arr, 'a\\(b', ['a(b']);
+    match(arr, 'a(*b', ['a(b', 'a((b', 'a((((b']);
+  });
+
+  it('should match escaped backslashes', function() {
+    match(['a(b', 'a\\(b', 'a((b', 'a((((b', 'ab'], 'a\\\\(b', ['a\\(b']);
+    match(['a\\b', 'a/b', 'ab'], 'a\\\\b', ['a\\b']);
+  });
+
+  // these are not extglobs, and do not need to pass, but they are included
+  // to test integration with expand-brackets
+  it('should match common regex patterns', function() {
+    var fixtures = ['a c', 'a1c', 'a123c', 'a.c', 'a.xy.zc', 'a.zc', 'abbbbc', 'abbbc', 'abbc', 'abc', 'abq', 'axy zc', 'axy', 'axy.zc', 'axyzc'];
+
+    match(['a\\b', 'a/b', 'ab'], 'a/b', ['a/b']);
+    match(fixtures, 'ab?bc', ['abbbc']);
+    match(fixtures, 'ab*c', ['abbbbc', 'abbbc', 'abbc', 'abc']);
+    match(fixtures, 'ab+bc', ['abbbbc', 'abbbc', 'abbc']);
+    match(fixtures, '^abc$', ['abc']);
+    match(fixtures, 'a.c', ['a.c']);
+    match(fixtures, 'a.*c', ['a.c', 'a.xy.zc', 'a.zc']);
+    match(fixtures, 'a*c', ['a c', 'a.c', 'a1c', 'a123c', 'abbbbc', 'abbbc', 'abbc', 'abc', 'axyzc', 'axy zc', 'axy.zc', 'a.xy.zc', 'a.zc']);
+    match(fixtures, 'a\\w+c', ['a1c', 'a123c', 'abbbbc', 'abbbc', 'abbc', 'abc', 'axyzc'], 'Should match word characters');
+    match(fixtures, 'a\\W+c', ['a.c', 'a c'], 'Should match non-word characters');
+    match(fixtures, 'a\\d+c', ['a1c', 'a123c'], 'Should match numbers');
+    match(['foo@#$%123ASD #$$%^&', 'foo!@#$asdfl;', '123'], '\\d+', ['123']);
+    match(['a123c', 'abbbc'], 'a\\D+c', ['abbbc'], 'Should match non-numbers');
+    match(['foo', ' foo '], '(f|o)+\\b', ['foo'], 'Should match word boundaries');
+  });
+});
+
+describe('bash unit tests', function() {
+  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'];
+
+  it('should match extended globs from the bash spec:', function() {
+    var f2 = ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx'];
+    match(f2, '!(foo)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(!(foo))', ['foo']);
+    match(f2, '!(!(!(foo)))', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo/bar', 'foobar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(!(!(!(foo))))', ['foo']);
+    match(f2, '!(!(foo))*', ['foo', 'foo/bar', 'foobar', 'foot', 'foox']);
+    match(f2, '!(f!(o))', ['fo']);
+    match(f2, '!(f(o))', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(f)', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(f)', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(foo)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(foo)*', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '!(x)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'xx']);
+    match(f2, '!(x)*', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox']);
+    match(f2, '*(!(f))', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '*((foo))', ['foo']);
+    match(f2, '+(!(f))', ['bar', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, '@(!(z*)|*x)', ['bar', 'f', 'fa', 'fb', 'ff', 'fff', 'fo', 'foo', 'foobar', 'foo/bar', 'foot', 'foox', 'o', 'of', 'ooo', 'ox', 'x', 'xx']);
+    match(f2, 'foo/!(foo)', ['foo/bar']);
+
+    match(fixtures, '(foo)bb', ['foobb']);
+    match(fixtures, '*(*(f)*(o))', [ 'ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof', 'ofoofo', 'ofooofoofofooo', 'oofooofo', 'ooo']);
+    match(fixtures, '*(*(of*(o)x)o)', [ 'ofoooxoofxo', 'ofoooxoofxoofoooxoofxo', 'ofoooxoofxoofoooxoofxoo', 'ofoooxoofxoofoooxoofxooofxofxo', 'ofxoofxo', 'ooo']);
+    match(fixtures, '*(f*(o))', ['ffffffo', 'fffooofoooooffoofffooofff', 'ffo', 'fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo', 'foooofof']);
+    match(fixtures, '*(f*(o)x)', ['foooxfooxfoxfooox', 'foooxfooxfxfooox', 'foox']);
+    match(fixtures, '*(f+(o))', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo', 'foooofo']);
+    match(fixtures, '*(of+(o))', ['ofoofo']);
+    match(fixtures, '*(of+(o)|f)', ['fofo', 'fofoofoofofoo', 'ofoofo', 'ofooofoofofooo']);
+    match(fixtures, '*(of|oof+(o))', ['ofoofo', 'oofooofo']);
+    match(fixtures, '*(oxf+(ox))', ['oxfoxoxfox']);
+    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', ' [...]
+    match(fixtures, '@(foo|f|fo)*(f|of+(o))', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo', 'fooofoofofooo']);
+
+    var arr = ['aaac', 'aac', 'ac', 'abbcd', 'abcd', 'acd', 'baaac', 'c', 'foo'];
+    match(arr, '*(@(a))a@(c)', ['aaac', 'aac', 'ac']);
+    match(arr, '@(ab|a*(b))*(c)d', ['abbcd', 'abcd', 'acd']);
+    match(arr, '?@(a|b)*@(c)d', ['abbcd', 'abcd']);
+    match(arr, '@(ab|a*@(b))*(c)d', ['abbcd', 'abcd']);
+    match(['aac'], '*(@(a))b@(c)', []);
+  });
+
+  it('should backtrack in alternation matches', function() {
+    match(fixtures, '*(fo|foo)', ['fofo', 'fofoofoofofoo', 'foo', 'foofoofo']);
+  });
+
+  it('should support exclusions', function() {
+    match(['foob', 'foobb', 'foo', 'bar', 'baz', 'foobar'], '!(foo)b*', ['bar', 'baz']);
+    match(['foo', 'bar', 'baz', 'foobar'], '*(!(foo))', ['bar', 'baz', 'foobar']);
+
+    // Bash 4.3 says this should match `foo` and `foobar` too
+    match(['foo', 'bar', 'baz', 'foobar'], '!(foo)*', ['bar', 'baz']);
+
+    match(['moo.cow', 'moo', 'cow'], '!(*.*)', ['moo', 'cow']);
+    match(['mad.moo.cow'], '!(*.*).!(*.*)', []);
+    match(['moo.cow', 'moo', 'cow'], '!(*.*).', []);
+    match(['moo.cow', 'moo', 'cow'], '.!(*.*)', []);
+    match(['mucca.pazza'], 'mu!(*(c))?.pa!(*(z))?', []);
+
+    match(['effgz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['effgz']);
+    match(['efgz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['efgz']);
+    match(['egz'], '@(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['egz']);
+    match(['egz'], '@(b+(c)d|e+(f)g?|?(h)i@(j|k))', []);
+    match(['egzefffgzbcdij'], '*(b+(c)d|e*(f)g?|?(h)i@(j|k))', ['egzefffgzbcdij']);
+  });
+
+  it('valid numbers', function() {
+    assert(match.isMatch('/dev/udp/129.22.8.102/45', '/dev/@(tcp|udp)/*/*'));
+    match(['0', '12', '1', '12abc', '555'], '[1-6]([0-9])', ['12']);
+    match(['0', '12', '1', '12abc', '555'], '[1-6]*([0-9])', ['1', '12', '555']);
+    match(['0', '12', '1', '12abc', '555'], '[1-5]*([6-9])', ['1']);
+    match(['0', '12', '1', '12abc', '555'], '0|[1-6]*([0-9])', ['0', '1', '12', '555']);
+    match(['07', '0377', '09'], '+([0-7])', ['0377', '07']);
+  });
+
+  it('stuff from korn\'s book', function() {
+    assert(!match.isMatch('para', 'para+([0-9])'));
+    assert(!match.isMatch('para381', 'para?([345]|99)1'));
+    assert(!match.isMatch('paragraph', 'para*([0-9])'));
+    assert(!match.isMatch('paragraph', 'para*([0-9])'));
+    assert(!match.isMatch('paramour', 'para@(chute|graph)'));
+    assert(match.isMatch('para', 'para*([0-9])'));
+    assert(match.isMatch('para.38', 'para!(*.[0-9])'));
+    assert(match.isMatch('para.graph', 'para!(*.[0-9])'));
+    assert(match.isMatch('para13829383746592', 'para*([0-9])'));
+    assert(match.isMatch('para39', 'para!(*.[0-9])'));
+    assert(match.isMatch('para987346523', 'para+([0-9])'));
+    assert(match.isMatch('para991', 'para?([345]|99)1'));
+    assert(match.isMatch('paragraph', 'para!(*.[0-9])'));
+    assert(match.isMatch('paragraph', 'para@(chute|graph)'));
+  });
+
+  it('tests derived from those in rosenblatt\'s korn shell book', function() {
+    assert(match.isMatch('', '*(0|1|3|5|7|9)'));
+    assert(match.isMatch('137577991', '*(0|1|3|5|7|9)'));
+    assert(!match.isMatch('2468', '*(0|1|3|5|7|9)'));
+
+    assert(!match.isMatch('file.C', '*.c?(c)'));
+    assert(!match.isMatch('file.ccc', '*.c?(c)'));
+    assert(match.isMatch('file.c', '*.c?(c)'));
+    assert(match.isMatch('file.cc', '*.c?(c)'));
+
+    assert(match.isMatch('parse.y', '!(*.c|*.h|Makefile.in|config*|README)'));
+    assert(match.isMatch('Makefile', '!(*.c|*.h|Makefile.in|config*|README)'));
+    assert(!match.isMatch('shell.c', '!(*.c|*.h|Makefile.in|config*|README)'));
+
+    assert(!match.isMatch('VMS.FILE;', '*\\;[1-9]*([0-9])'));
+    assert(!match.isMatch('VMS.FILE;0', '*\\;[1-9]*([0-9])'));
+    assert(!match.isMatch('VMS.FILE;1N', '*\\;[1-9]*([0-9])'));
+    assert(match.isMatch('VMS.FILE;1', '*\\;[1-9]*([0-9])'));
+    assert(match.isMatch('VMS.FILE;139', '*\\;[1-9]*([0-9])'));
+  });
+
+  it('tests derived from the pd-ksh test suite', function() {
+    match(['abcx', 'abcz', 'bbc'], '!([*)*', ['abcx', 'abcz', 'bbc']);
+    match(['abcx', 'abcz', 'bbc'], '+(a|b[)*', ['abcx', 'abcz']);
+    match(['abcx', 'abcz', 'bbc'], '[a*(]*)z', []);
+
+    match(['abc'], '+()c', []);
+    match(['abc'], '+()x', []);
+    match(['abc'], '+(*)c', ['abc']);
+    match(['abc'], '+(*)x', []);
+
+    match(['abc'], 'no-file+(a|b)stuff', []);
+    match(['abc'], 'no-file+(a*(c)|b)stuff', []);
+
+    match(['abd', 'acd'], 'a+(b|c)d', ['abd', 'acd']);
+    match(['abc'], 'a+(b|c)d', []);
+
+    match(['acd'], 'a!(@(b|B))d', ['acd']);
+    match(['abc', 'abd'], 'a!(@(b|B))d', []);
+
+    match(['abd'], 'a[b*(foo|bar)]d', ['abd']);
+    match(['abc', 'acd'], 'a[b*(foo|bar)]d', []);
+  });
+
+  it('simple kleene star tests', function() {
+    assert(!match.isMatch('foo', '*(a|b[)'));
+    assert(!match.isMatch('(', '*(a|b[)'));
+    assert(!match.isMatch(')', '*(a|b[)'));
+    assert(!match.isMatch('|', '*(a|b[)'));
+    assert(match.isMatch('a', '*(a|b)'));
+    assert(match.isMatch('b', '*(a|b)'));
+    assert(match.isMatch('b[', '*(a|b\\[)'));
+    assert(match.isMatch('ab[', '+(a|b\\[)'));
+    assert(!match.isMatch('ab[cde', '+(a|b\\[)'));
+    assert(match.isMatch('ab[cde', '+(a|b\\[)*'));
+  });
+
+  it('check extended globbing in pattern removal', function() {
+    match(['a', 'abc'], '+(a|abc)', ['a', 'abc']);
+    match(['abcd', 'abcde', 'abcedf'], '+(a|abc)', []);
+
+    match(['f'], '+(def|f)', ['f']);
+
+    match(['def'], '+(f|def)', ['def']);
+    match(['cdef', 'bcdef', 'abcedf'], '+(f|def)', []);
+
+    match(['abcd'], '*(a|b)cd', ['abcd']);
+    match(['a', 'ab', 'abc'], '*(a|b)cd', []);
+
+    match(['a', 'ab', 'abc', 'abcde', 'abcdef'], '"*(a|b)cd"', []);
+  });
+
+  it('More tests derived from a bug report (in bash) concerning extended glob patterns following a *', function() {
+    var fixtures = ['123abc', 'ab', 'abab', 'abcdef', 'accdef', 'abcfefg', 'abef', 'abcfef', 'abd', 'acd'];
+    match(['/dev/udp/129.22.8.102/45'], '/dev\\/@(tcp|udp)\\/*\\/*', ['/dev/udp/129.22.8.102/45']);
+    match(fixtures, '(a+|b)*', ['ab', 'abab', 'accdef', 'abcdef', 'abcfefg', 'abef', 'abcfef', 'abd', 'acd']);
+    match(fixtures, '(a+|b)+', ['ab', 'abab']);
+    match(fixtures, 'a(b*(foo|bar))d', ['abd']);
+    match(fixtures, 'ab*(e|f)', ['ab', 'abef']);
+    match(fixtures, 'ab**(e|f)', ['ab', 'abab', 'abcdef', 'abcfef', 'abd', 'abef', 'abcfefg']);
+    match(fixtures, 'ab**(e|f)g', ['abcfefg']);
+    match(fixtures, 'ab***ef', ['abcdef', 'abcfef', 'abef']);
+    match(fixtures, 'ab*+(e|f)', ['abcdef', 'abcfef', 'abef']);
+    match(fixtures, 'ab*d*(e|f)', ['abcdef', 'abd']);
+    match(fixtures, 'ab*d+(e|f)', ['abcdef']);
+    match(fixtures, 'ab?*(e|f)', ['abcfef', 'abd', 'abef']);
+  });
+
+  it('bug in all versions up to and including bash-2.05b', function() {
+    assert(match.isMatch('123abc', '*?(a)bc'));
+  });
+
+  it('should work with character classes', function() {
+    var fixtures = ['a.b', 'a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b'];
+    match(fixtures, 'a[^[:alnum:]]b', fixtures);
+    match(fixtures, 'a[-.,:\\;\\ _]b', fixtures);
+    match(fixtures, 'a@([^[:alnum:]])b', fixtures);
+    match(fixtures, 'a@([-.,:; _])b', fixtures);
+    match(fixtures, 'a@([.])b', ['a.b']);
+    match(fixtures, 'a@([^.])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']);
+    match(fixtures, 'a@([^x])b', ['a,b', 'a:b', 'a-b', 'a;b', 'a b', 'a_b']);
+    match(fixtures, 'a+([^[:alnum:]])b', fixtures);
+    match(fixtures, 'a@(.|[^[:alnum:]])b', fixtures);
+  });
+
+  it('should support POSIX character classes in extglobs', function() {
+    assert(match.isMatch('a.c', '+([[:alpha:].])'));
+    assert(match.isMatch('a.c', '+([[:alpha:].])+([[:alpha:].])'));
+    assert(match.isMatch('a.c', '*([[:alpha:].])'));
+    assert(match.isMatch('a.c', '*([[:alpha:].])*([[:alpha:].])'));
+    assert(match.isMatch('a.c', '?([[:alpha:].])?([[:alpha:].])?([[:alpha:].])'));
+    assert(match.isMatch('a.c', '@([[:alpha:].])@([[:alpha:].])@([[:alpha:].])'));
+    assert(!match.isMatch('.', '!(\\.)'));
+    assert(!match.isMatch('.', '!([[:alpha:].])'));
+    assert(match.isMatch('.', '?([[:alpha:].])'));
+    assert(match.isMatch('.', '@([[:alpha:].])'));
+  });
+
+  // ported from http://www.bashcookbook.com/bashinfo/source/bash-4.3/tests/extglob2.tests
+  it('should pass extglob2 tests', function() {
+    assert(!match.isMatch('baaac', '*(@(a))a@(c)'));
+    assert(!match.isMatch('c', '*(@(a))a@(c)'));
+    assert(!match.isMatch('egz', '@(b+(c)d|e+(f)g?|?(h)i@(j|k))'));
+    assert(!match.isMatch('foooofof', '*(f+(o))'));
+    assert(!match.isMatch('foooofofx', '*(f*(o))'));
+    assert(!match.isMatch('foooxfooxofoxfooox', '*(f*(o)x)'));
+    assert(!match.isMatch('ofooofoofofooo', '*(f*(o))'));
+    assert(!match.isMatch('ofoooxoofxoofoooxoofxofo', '*(*(of*(o)x)o)'));
+    assert(!match.isMatch('oxfoxfox', '*(oxf+(ox))'));
+    assert(!match.isMatch('xfoooofof', '*(f*(o))'));
+    assert(match.isMatch('aaac', '*(@(a))a@(c)'));
+    assert(match.isMatch('aac', '*(@(a))a@(c)'));
+    assert(match.isMatch('abbcd', '@(ab|a*(b))*(c)d'));
+    assert(match.isMatch('abcd', '?@(a|b)*@(c)d'));
+    assert(match.isMatch('abcd', '@(ab|a*@(b))*(c)d'));
+    assert(match.isMatch('ac', '*(@(a))a@(c)'));
+    assert(match.isMatch('acd', '@(ab|a*(b))*(c)d'));
+    assert(match.isMatch('effgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))'));
+    assert(match.isMatch('efgz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))'));
+    assert(match.isMatch('egz', '@(b+(c)d|e*(f)g?|?(h)i@(j|k))'));
+    assert(match.isMatch('egzefffgzbcdij', '*(b+(c)d|e*(f)g?|?(h)i@(j|k))'));
+    assert(match.isMatch('fffooofoooooffoofffooofff', '*(*(f)*(o))'));
+    assert(match.isMatch('ffo', '*(f*(o))'));
+    assert(match.isMatch('fofo', '*(f*(o))'));
+    assert(match.isMatch('foofoofo', '@(foo|f|fo)*(f|of+(o))'));
+    assert(match.isMatch('fooofoofofooo', '*(f*(o))'));
+    assert(match.isMatch('foooofo', '*(f*(o))'));
+    assert(match.isMatch('foooofof', '*(f*(o))'));
+    assert(match.isMatch('foooxfooxfoxfooox', '*(f*(o)x)'));
+    assert(match.isMatch('foooxfooxfxfooox', '*(f*(o)x)'));
+    assert(match.isMatch('ofoofo', '*(of+(o))'));
+    assert(match.isMatch('ofoofo', '*(of+(o)|f)'));
+    assert(match.isMatch('ofoooxoofxo', '*(*(of*(o)x)o)'));
+    assert(match.isMatch('ofoooxoofxoofoooxoofxo', '*(*(of*(o)x)o)'));
+    assert(match.isMatch('ofoooxoofxoofoooxoofxoo', '*(*(of*(o)x)o)'));
+    assert(match.isMatch('ofoooxoofxoofoooxoofxooofxofxo', '*(*(of*(o)x)o)'));
+    assert(match.isMatch('ofxoofxo', '*(*(of*(o)x)o)'));
+    assert(match.isMatch('oofooofo', '*(of|oof+(o))'));
+    assert(match.isMatch('oxfoxoxfox', '*(oxf+(ox))'));
+  });
+
+  it('should support backtracking in alternation matches', function() {
+    assert(match.isMatch('fofoofoofofoo', '*(fo|foo)'));
+  });
+
+  it('should support exclusions', function() {
+    assert(!match.isMatch('f', '!(f)'));
+    assert(!match.isMatch('f', '*(!(f))'));
+    assert(!match.isMatch('f', '+(!(f))'));
+    assert(!match.isMatch('foo', '!(foo)'));
+    assert(!match.isMatch('foob', '!(foo)b*'));
+    assert(!match.isMatch('mad.moo.cow', '!(*.*).!(*.*)'));
+    assert(!match.isMatch('mucca.pazza', 'mu!(*(c))?.pa!(*(z))?'));
+    assert(!match.isMatch('zoot', '@(!(z*)|*x)'));
+    assert(match.isMatch('fff', '!(f)'));
+    assert(match.isMatch('fff', '*(!(f))'));
+    assert(match.isMatch('fff', '+(!(f))'));
+    assert(match.isMatch('foo', '!(f)'));
+    assert(match.isMatch('foo', '!(x)'));
+    assert(match.isMatch('foo', '!(x)*'));
+    assert(match.isMatch('foo', '*(!(f))'));
+    assert(match.isMatch('foo', '+(!(f))'));
+    assert(match.isMatch('foobar', '!(foo)'));
+    assert(match.isMatch('foot', '@(!(z*)|*x)'));
+    assert(match.isMatch('foox', '@(!(z*)|*x)'));
+    assert(match.isMatch('ooo', '!(f)'));
+    assert(match.isMatch('ooo', '*(!(f))'));
+    assert(match.isMatch('ooo', '+(!(f))'));
+    assert(match.isMatch('zoox', '@(!(z*)|*x)'));
+  });
+
+  it('should pass extglob3 tests', function() {
+    assert(match.isMatch('ab/../', '+(??)/..?(/)'));
+    assert(match.isMatch('ab/../', '+(??|a*)/..?(/)'));
+    assert(match.isMatch('ab/../', '+(?b)/..?(/)'));
+    assert(match.isMatch('ab/../', '+(?b|?b)/..?(/)'));
+    assert(match.isMatch('ab/../', '+([!/])/../'));
+    assert(match.isMatch('ab/../', '+([!/])/..?(/)'));
+    assert(match.isMatch('ab/../', '+([!/])/..@(/)'));
+    assert(match.isMatch('ab/../', '+([^/])/../'));
+    assert(match.isMatch('ab/../', '+([^/])/..?(/)'));
+    assert(match.isMatch('ab/../', '+(a*)/..?(/)'));
+    assert(match.isMatch('ab/../', '+(ab)/..?(/)'));
+    assert(match.isMatch('ab/../', '?(ab)/..?(/)'));
+    assert(match.isMatch('ab/../', '?(ab|??)/..?(/)'));
+    assert(match.isMatch('ab/../', '?b/..?(/)'));
+    assert(match.isMatch('ab/../', '@(??)/..?(/)'));
+    assert(match.isMatch('ab/../', '@(??|a*)/..?(/)'));
+    assert(match.isMatch('ab/../', '@(?b|?b)/..?(/)'));
+    assert(match.isMatch('ab/../', '@(a*)/..?(/)'));
+    assert(match.isMatch('ab/../', '@(a?|?b)/..?(/)'));
+    assert(match.isMatch('ab/../', '@(ab|+([!/]))/..?(/)'));
+    assert(match.isMatch('ab/../', '@(ab|+([^/]))/..?(/)'));
+    assert(match.isMatch('ab/../', '@(ab|?b)/..?(/)'));
+    assert(match.isMatch('ab/../', '[!/][!/]/../'));
+    assert(match.isMatch('ab/../', '[^/][^/]/../'));
+    assert(match.isMatch('x', '@(x)'));
+  });
+});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-extglob.git



More information about the Pkg-javascript-commits mailing list