[Pkg-javascript-commits] [node-braces] 01/05: Import Upstream version 2.0.2

Sruthi Chandran srud-guest at moszumanska.debian.org
Thu Nov 3 07:25:30 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-braces.

commit 0eef8824357e9987496a34406a872200017a2006
Author: Sruthi <srud at disroot.org>
Date:   Thu Nov 3 12:29:09 2016 +0530

    Import Upstream version 2.0.2
---
 .editorconfig                            |  13 +
 .eslintrc.json                           | 122 ++++++++
 .gitattributes                           |  14 +
 .gitignore                               |  20 ++
 .travis.yml                              |  11 +
 .verb.md                                 | 360 +++++++++++++++++++++++
 LICENSE                                  |  21 ++
 README.md                                | 486 +++++++++++++++++++++++++++++++
 appveyor.yml                             |  26 ++
 benchmark/code/brace-expansion.js        |   4 +
 benchmark/code/braces.js                 |   4 +
 benchmark/code/minimatch.js              |   4 +
 benchmark/fixtures/combination-nested.js |   1 +
 benchmark/fixtures/combination.js        |   1 +
 benchmark/fixtures/escaped.js            |   1 +
 benchmark/fixtures/list-basic.js         |   1 +
 benchmark/fixtures/list-multiple.js      |   1 +
 benchmark/fixtures/match.multiple.js     |   1 +
 benchmark/fixtures/match.sequence.js     |   1 +
 benchmark/fixtures/no-braces.js          |   1 +
 benchmark/fixtures/sequence-basic.js     |   1 +
 benchmark/fixtures/sequence-multiple.js  |   1 +
 benchmark/index.js                       |  30 ++
 benchmark/last.md                        |  65 +++++
 examples.md                              |  60 ++++
 examples/comparison.js                   |  33 +++
 examples/escape.js                       |   4 +
 examples/expand.js                       |   7 +
 examples/nested.js                       |  12 +
 examples/options.quantifiers.js          |  11 +
 examples/regex.js                        |  17 ++
 examples/sequences-steps.js              |   7 +
 gulpfile.js                              |  32 ++
 index.js                                 | 300 +++++++++++++++++++
 lib/braces.js                            | 133 +++++++++
 lib/compilers.js                         | 270 +++++++++++++++++
 lib/parsers.js                           | 238 +++++++++++++++
 lib/utils.js                             | 253 ++++++++++++++++
 package.json                             | 116 ++++++++
 test/bash.expanded.js                    | 411 ++++++++++++++++++++++++++
 test/bash.optimized.js                   | 409 ++++++++++++++++++++++++++
 test/bash.spec.js                        | 188 ++++++++++++
 test/brace-expansion.js                  |  91 ++++++
 test/braces.compile.js                   |  19 ++
 test/braces.js                           |  58 ++++
 test/braces.parse.js                     |  18 ++
 test/expanded.integration.js             |  27 ++
 test/expanded.ranges.js                  | 187 ++++++++++++
 test/expanded.sets.js                    | 204 +++++++++++++
 test/minimatch.js                        |  28 ++
 test/multiples.js                        |  60 ++++
 test/optimized.js                        | 350 ++++++++++++++++++++++
 test/options.js                          |  73 +++++
 test/regression-1.8.5.js                 | 463 +++++++++++++++++++++++++++++
 test/support/bash.js                     |  59 ++++
 test/support/generate.js                 |  57 ++++
 test/support/utils.js                    |  59 ++++
 57 files changed, 5444 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..759c2c5
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,14 @@
+# Enforce Unix newlines
+*.*     text eol=lf
+*.css   text eol=lf
+*.html  text eol=lf
+*.js    text eol=lf
+*.json  text eol=lf
+*.less  text eol=lf
+*.md    text eol=lf
+*.yml   text eol=lf
+
+*.jpg binary
+*.gif binary
+*.png binary
+*.jpeg binary
\ 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..97af4a3
--- /dev/null
+++ b/.verb.md
@@ -0,0 +1,360 @@
+## Usage
+
+The main export is a function that takes a brace `pattern` to expand and an `options` object if necessary.
+
+```js
+var braces = require('{%= name %}');
+braces(pattern[, options]);
+```
+
+## Highlights
+
+- **Safer**: Braces isn't [vulnerable to DoS attacks](#braces-is-safe), unlike [brace-expansion][], [minimatch][] and [multimatch][] (this not the same [DoS bug][bug] that was found in minimatch and multimatch recently)
+- **Accurate**: complete support for the [Bash 4.3 Brace Expansion][bash] specification (passes all of the Bash braces tests)
+- **[fast and performant](#benchmarks)**: not only runs fast, but scales well as patterns increase in complexity (other libs don't - see the notes on [performance](#performance)). 
+- **Organized code base**: with parser and compiler that are easy to maintain and update when edge cases crop up.
+- **Well-tested**: 900+ unit tests with thousands of actual patterns tested. Passes 100% of the [minimatch][] and [brace-expansion][] unit tests as well.
+- **Optimized braces**: By default returns an optimized string that can be used for creating regular expressions for matching. 
+- **Expanded braces**: Optionally returns an array (like bash). See a [comparison](#optimized-vs-expanded) between optimized and expanded.
+
+
+**Optimized**
+
+Patterns are optimized for regex and matching by default:
+
+```js
+console.log(braces('a/{x,y,z}/b'));
+//=> ['a/(x|y|z)/b']
+```
+
+**Expanded**
+
+To expand patterns the same way as Bash or [minimatch][], use the [.expand](#expand) method:
+
+```js
+console.log(braces.expand('a/{x,y,z}/b'));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
+
+
+## Features
+
+- [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
+- [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']` 
+- [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
+- [escaping](#escaping)
+- [options](#options)
+
+
+### Lists
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric lists:
+
+```js
+console.log(braces('a/{foo,bar,baz}/*.js'));
+//=> ['a/(foo|bar|baz)/*.js']
+
+console.log(braces.expand('a/{foo,bar,baz}/*.js'));
+//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
+```
+
+### Sequences
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
+
+```js
+// padding is not retained when string is optimized
+console.log(braces('a{01..03}b')); // ['a([1-3])b']
+console.log(braces('a{1..3}b'));   // ['a([1-3])b']
+
+console.log(braces.expand('{1..3}'));     // ['1', '2', '3']
+console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
+console.log(braces.expand('a{1..3}b'));   // ['a1b', 'a2b', 'a3b']
+console.log(braces.expand('{a..c}'));     // ['a', 'b', 'c']
+console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
+```
+
+### Steps
+
+Steps, or increments, may be used with ranges:
+
+```js
+console.log(braces.expand('{2..10..2}'));
+//=> ['2', '4', '6', '8', '10']
+
+console.log(braces('{2..10..2}'));
+//=> ['(2|4|6|8|10)']
+```
+
+When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range][] for expansion.
+
+### Nesting
+
+Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved. 
+
+**"Expanded" examples**
+
+```js
+console.log(braces.expand('a{b,c,/{x,y}}/e'));
+//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
+
+console.log(braces.expand('a/{x,{1..5},y}/c'));
+//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
+```
+
+**"Optimized" examples**
+
+```js
+console.log(braces('a{b,c,/{x,y}}/e'));
+//=> ['a(b|c|/(x|y))/e']
+
+console.log(braces('a/{x,{1..5},y}/c'));
+//=> ['a/(x|([1-5])|y)/c']
+```
+
+### Escaping
+
+**Escaping braces**
+
+Prevent braces from being expanded or evaluted by escaping either the opening or closing brace:
+
+```js
+console.log(braces.expand('a\\{d,c,b}e'));
+//=> ['a{d,c,b}e']
+
+console.log(braces.expand('a{d,c,b\\}e'));
+//=> ['a{d,c,b}e']
+```
+
+**Escaping commas**
+
+Commas inside braces may also be escaped:
+
+```js
+console.log(braces.expand('a{d\\,c,b}e'));
+//=> ['ad,ce', 'abe']
+```
+
+**Single items**
+
+A brace pattern is also considered to be escaped when it contains a single item: 
+
+```js
+console.log(braces.expand('a{b}c'));
+//=> ['a{b}c']
+
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+```
+
+## Options
+
+### options.expand
+
+Type: `Boolean`
+
+Default: `undefined`
+
+Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
+
+```js
+console.log(braces('a/{b,c}/d', {expand: true}));
+//=> [ 'a/b/d', 'a/c/d' ]
+```
+
+### options.optimize
+
+Type: `Boolean`
+
+Default: `true`
+
+Enabled by default.
+
+```js
+console.log(braces('a/{b,c}/d'));
+//=> [ 'a/(b|c)/d' ]
+```
+
+### options.nodupes
+
+Type: `Boolean`
+
+Default: `true`
+
+Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
+
+
+### options.rangeLimit
+
+Type: `Number`
+
+Default: `250`
+
+When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
+
+You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
+
+**Examples**
+
+```js
+// pattern exceeds the "rangeLimit", so it's optimized automatically
+console.log(braces.expand('{1..1000}'));
+//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
+
+// pattern does not exceed "rangeLimit", so it's NOT optimized
+console.log(braces.expand('{1..100}'));
+//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83 [...]
+```
+
+### options.transform
+
+Type: `Function`
+
+Default: `undefined`
+
+Customize range expansion.
+
+```js
+var range = braces.expand('x{a..e}y', {
+  transform: function(str) {
+    return 'foo' + str;
+  }
+});
+
+console.log(range);
+//=> [ 'xfooay', 'xfooby', 'xfoocy', 'xfoody', 'xfooey' ]
+```
+
+### options.quantifiers
+
+Type: `Boolean`
+
+Default: `undefined`
+
+In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+
+Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
+
+The `quantifiers` option tells braces to detect when [regex quantifiers][quantifiers] are defined in the given pattern, and not to try to expand them as lists.
+
+**Examples**
+
+```js
+var braces = require('braces');
+console.log(braces('a/b{1,3}/{x,y,z}'));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
+```
+
+## Benchmarks
+
+### Running benchmarks
+
+Install dev dependencies:
+
+```bash
+npm i -d && npm benchmark
+```
+
+### Latest results
+
+```bash
+{%= docs("benchmark/last.md") %}
+```
+
+## Performance
+
+### What's the big deal?
+
+If you use globbing a lot, whether in your build tool chain or in your application, the speed of the glob matcher not only impacts your experience, but a slow glob matcher can slow everything else down, limitimg what you thought was possible in your application.
+
+### Braces is fast
+
+> minimatch gets exponentially slower as patterns increase in complexity, braces does not
+
+Brace patterns are meant to be expanded - at least, if you're using Bash, that is. It's not at all unusual for users to want to use brace patterns for dates or similar ranges. It's also very easy to define a brace pattern that appears to be very simple but in fact is fairly complex. 
+
+For example, here is how generated regex size and processing time compare as patterns increase in complexity: 
+
+_(the following results were generated using `braces()` and `minimatch.braceExpand()`)_
+
+| **Pattern** | **minimatch** | **braces** |
+| ---                     | ---                        | ---                  |
+| `{1..9007199254740991}`[^1] | N/A (freezes)          | `300 B` (12ms 878μs) |
+| `{1..10000000}`         | `98.9 MB` (20s 193ms 13μs) | `34 B` (11ms 204μs)  |
+| `{1..1000000}`          | `8.89 MB` (1s 838ms 718μs) | `33 B` (1ms 761μs)   |
+| `{1..100000}`           | `789 kB` (181ms 518μs)     | `32 B` (1ms 76μs)    |
+| `{1..10000}`            | `68.9 kB` (17ms 436μs)     | `31 B` (1ms 382μs)   |
+| `{1..1000}`             | `5.89 kB` (1ms 773μs)      | `30 B` (1ms 509μs)   |
+| `{1..100}`              | `491 B` (321μs)            | `24 B` (309μs)       |
+| `{1..10}`               | `40 B` (56μs)              | `12 B` (333μs)       |
+| `{1..3}`                | `11 B` (80μs)              | `9 B` (370μs)        |
+
+These numbers are actually pretty small as far as numeric ranges are concerned. Regardless, we shouldn't have to consider such things when creating a glob pattern. The tool should get out of your way and let you be as creative as you want.
+
+### Braces is performant
+
+Even when brace patterns are fully **expanded**, `braces` is still much faster. 
+
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`)_
+
+| **Pattern** | **minimatch** | **braces** |
+| ---               | ---                         | ---                        |
+| `a/{1..10000000}` | `98.9 MB` (19s 754ms 376μs) | `98.9 MB` (5s 734ms 419μs) |
+| `a/{1..1000000}`  | `8.89 MB` (1s 866ms 968μs)  | `8.89 MB` (561ms 594μs)    |
+| `a/{1..100000}`   | `789 kB` (178ms 311μs)      | `789 kB` (29ms 823μs)      |
+| `a/{1..10000}`    | `68.9 kB` (17ms 692μs)      | `68.9 kB` (2ms 351μs)      |
+| `a/{1..1000}`     | `5.89 kB` (1ms 823μs)       | `5.89 kB` (706μs)          |
+| `a/{1..100}`      | `491 B` (609μs)             | `491 B` (267μs)            |
+| `a/{1..10}`       | `40 B` (61μs)               | `40 B` (636μs)             |
+| `a/{1..3}`        | `11 B` (206μs)              | `11 B` (207μs)             |
+
+### Why is braces so fast?
+
+Braces was built from the ground up to be as performant as possible when matching, using a combination of the following:
+
+- **minimizes loops and iterating**: we try to pass over the pattern once to parse it before passing it to the compiler.
+- **generates an optimized string**: sequences/ranges are optimized by [to-regex-range][], which is not only highly accurate, but produces patterns that are a fraction of the size of patterns generated by other brace expansion libraries, such as Bash and [minimatch][] (via [brace-expansion][])
+- **generates results faster**: can handle even the most complex patterns that cause other implementations like [minimatch][] and Bash to fail. 
+
+### Braces is safe
+
+Last, but perhaps most important of all, unlike [brace-expansion][] and [minimatch][], braces will not freeze your application when a user passes a complex brace pattern.
+
+**The trouble with ~~tribbles~~ brace patterns**
+
+There are unlimited scenarios where brace patterns cause the resulting array to grow geometrically. If you define two brace patterns for example, the result is multiplicative if not exponential. For example, given the pattern `{1..1000}`, we would end up with 1 thousand strings. But if two patterns are given, such as `{1...1000}{1...1000}`, we would end up with 1 million strings. 
+
+It's easy to think, "I wouldn't do that", but that's not the point. Even though those example patterns are contrived:
+
+1. it's not uncommon for users to define brace patterns that result in similarly huge strings (consider date ranges `1-2016`, for example, and how easy it might be for someone to also add an alphabetical range if searching records of some kind)
+1. we can't control how users define their patterns (and we shouldn't have to care)
+1. even if it's uncommon for user to define unwieldy patterns, there is still potential for intentionally exploiting this feature to [cause a DoS][dos] in your application (and given that this is a not-very-technically-advanced method for causing a DoS - e.g. anyone can do it, you should be concerned!)
+
+**Potential for DoS attacks**
+
+Most brace expansion libraries, including Bash, [minimatch][], [multimatch][] and [brace-expansion](https://github.com/juliangruber/brace-expansion) are vulnerable to DoS attacks (similar to the [ReDoS bug][bug] minimatch had recently).
+
+Braces mitigates this risk in three ways: 
+
+1. Braces prevents malicious strings from being used by checking the length of the input strings, as well as generated regex
+1. Braces [optimizes](#optimize) the generated result by default, so that only _one output string is generated for every input string_.
+1. When [.expand](#expand) or [options.expand](#optionsexpand) are used, braces has checks in place to prevent huge ranges from being (accidentally) created. By default, when more than 250 strings will result from the given pattern, braces will [optimize](#optimize) the result instead of expanding it. You can override this limit of 250 by passing a custom number on [options.rangeLimit](#optionsrangelimit).
+
+_(you might also want to consider using [micromatch][] for glob matching, as a safer alternative to minimatch and multimatch)_
+
+[^1]: this is the largest safe integer allowed in JavaScript. 
+
+[bash]: www.gnu.org/software/bash/
+[braces]: https://github.com/jonschlinkert/braces
+[brace-expansion]: https://github.com/juliangruber/brace-expansion
+[expand-range]: https://github.com/jonschlinkert/expand-range
+[fill-range]: https://github.com/jonschlinkert/fill-range
+[micromatch]: https://github.com/jonschlinkert/micromatch
+[minimatch]: https://github.com/isaacs/minimatch
+[quantifiers]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers
+[dos]: https://en.wikipedia.org/wiki/Denial-of-service_attack
+[bug]: https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..39245ac
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-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..dd944a2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,486 @@
+# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces)  [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/braces.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/braces) [ [...]
+
+> Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces specification, without sacrificing speed.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save braces
+```
+
+## Usage
+
+The main export is a function that takes a brace `pattern` to expand and an `options` object if necessary.
+
+```js
+var braces = require('braces');
+braces(pattern[, options]);
+```
+
+## Highlights
+
+* **Safer**: Braces isn't [vulnerable to DoS attacks](#braces-is-safe), unlike [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch) and [multimatch](https://github.com/sindresorhus/multimatch) (this not the same [DoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc) that was found in minimatch and multimatch recently)
+* **Accurate**: complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
+* **[fast and performant](#benchmarks)**: not only runs fast, but scales well as patterns increase in complexity (other libs don't - see the notes on [performance](#performance)).
+* **Organized code base**: with parser and compiler that are easy to maintain and update when edge cases crop up.
+* **Well-tested**: 900+ unit tests with thousands of actual patterns tested. Passes 100% of the [minimatch](https://github.com/isaacs/minimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests as well.
+* **Optimized braces**: By default returns an optimized string that can be used for creating regular expressions for matching.
+* **Expanded braces**: Optionally returns an array (like bash). See a [comparison](#optimized-vs-expanded) between optimized and expanded.
+
+**Optimized**
+
+Patterns are optimized for regex and matching by default:
+
+```js
+console.log(braces('a/{x,y,z}/b'));
+//=> ['a/(x|y|z)/b']
+```
+
+**Expanded**
+
+To expand patterns the same way as Bash or [minimatch](https://github.com/isaacs/minimatch), use the [.expand](#expand) method:
+
+```js
+console.log(braces.expand('a/{x,y,z}/b'));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
+
+## Features
+
+* [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
+* [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']`
+* [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
+* [escaping](#escaping)
+* [options](#options)
+
+### Lists
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric lists:
+
+```js
+console.log(braces('a/{foo,bar,baz}/*.js'));
+//=> ['a/(foo|bar|baz)/*.js']
+
+console.log(braces.expand('a/{foo,bar,baz}/*.js'));
+//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
+```
+
+### Sequences
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
+
+```js
+// padding is not retained when string is optimized
+console.log(braces('a{01..03}b')); // ['a([1-3])b']
+console.log(braces('a{1..3}b'));   // ['a([1-3])b']
+
+console.log(braces.expand('{1..3}'));     // ['1', '2', '3']
+console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
+console.log(braces.expand('a{1..3}b'));   // ['a1b', 'a2b', 'a3b']
+console.log(braces.expand('{a..c}'));     // ['a', 'b', 'c']
+console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
+```
+
+### Steps
+
+Steps, or increments, may be used with ranges:
+
+```js
+console.log(braces.expand('{2..10..2}'));
+//=> ['2', '4', '6', '8', '10']
+
+console.log(braces('{2..10..2}'));
+//=> ['(2|4|6|8|10)']
+```
+
+When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
+
+### Nesting
+
+Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
+
+**"Expanded" examples**
+
+```js
+console.log(braces.expand('a{b,c,/{x,y}}/e'));
+//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
+
+console.log(braces.expand('a/{x,{1..5},y}/c'));
+//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
+```
+
+**"Optimized" examples**
+
+```js
+console.log(braces('a{b,c,/{x,y}}/e'));
+//=> ['a(b|c|/(x|y))/e']
+
+console.log(braces('a/{x,{1..5},y}/c'));
+//=> ['a/(x|([1-5])|y)/c']
+```
+
+### Escaping
+
+**Escaping braces**
+
+Prevent braces from being expanded or evaluted by escaping either the opening or closing brace:
+
+```js
+console.log(braces.expand('a\\{d,c,b}e'));
+//=> ['a{d,c,b}e']
+
+console.log(braces.expand('a{d,c,b\\}e'));
+//=> ['a{d,c,b}e']
+```
+
+**Escaping commas**
+
+Commas inside braces may also be escaped:
+
+```js
+console.log(braces.expand('a{d\\,c,b}e'));
+//=> ['ad,ce', 'abe']
+```
+
+**Single items**
+
+A brace pattern is also considered to be escaped when it contains a single item:
+
+```js
+console.log(braces.expand('a{b}c'));
+//=> ['a{b}c']
+
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+```
+
+## Options
+
+### options.expand
+
+Type: `Boolean`
+
+Default: `undefined`
+
+Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
+
+```js
+console.log(braces('a/{b,c}/d', {expand: true}));
+//=> [ 'a/b/d', 'a/c/d' ]
+```
+
+### options.optimize
+
+Type: `Boolean`
+
+Default: `true`
+
+Enabled by default.
+
+```js
+console.log(braces('a/{b,c}/d'));
+//=> [ 'a/(b|c)/d' ]
+```
+
+### options.nodupes
+
+Type: `Boolean`
+
+Default: `true`
+
+Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
+
+### options.rangeLimit
+
+Type: `Number`
+
+Default: `250`
+
+When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
+
+You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
+
+**Examples**
+
+```js
+// pattern exceeds the "rangeLimit", so it's optimized automatically
+console.log(braces.expand('{1..1000}'));
+//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
+
+// pattern does not exceed "rangeLimit", so it's NOT optimized
+console.log(braces.expand('{1..100}'));
+//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83 [...]
+```
+
+### options.transform
+
+Type: `Function`
+
+Default: `undefined`
+
+Customize range expansion.
+
+```js
+var range = braces.expand('x{a..e}y', {
+  transform: function(str) {
+    return 'foo' + str;
+  }
+});
+
+console.log(range);
+//=> [ 'xfooay', 'xfooby', 'xfoocy', 'xfoody', 'xfooey' ]
+```
+
+### options.quantifiers
+
+Type: `Boolean`
+
+Default: `undefined`
+
+In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+
+Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
+
+The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
+
+**Examples**
+
+```js
+var braces = require('braces');
+console.log(braces('a/b{1,3}/{x,y,z}'));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
+```
+
+## Benchmarks
+
+### Running benchmarks
+
+Install dev dependencies:
+
+```bash
+npm i -d && npm benchmark
+```
+
+### Latest results
+
+```bash
+Benchmarking: (8 of 8)
+ · combination-nested
+ · combination
+ · escaped
+ · list-basic
+ · list-multiple
+ · no-braces
+ · sequence-basic
+ · sequence-multiple
+
+# benchmark/fixtures/combination-nested.js (52 bytes)
+  brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
+  braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
+  minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/combination.js (51 bytes)
+  brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
+  braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
+  minimatch x 637 ops/sec ±0.84% (88 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/escaped.js (44 bytes)
+  brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
+  braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
+  minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/list-basic.js (40 bytes)
+  brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
+  braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
+  minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/list-multiple.js (52 bytes)
+  brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
+  braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
+  minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/no-braces.js (48 bytes)
+  brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
+  braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
+  minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/sequence-basic.js (41 bytes)
+  brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
+  braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
+  minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/sequence-multiple.js (51 bytes)
+  brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
+  braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
+  minimatch x 109 ops/sec ±1.16% (76 runs sampled)
+
+  fastest is braces
+```
+
+## Performance
+
+### What's the big deal?
+
+If you use globbing a lot, whether in your build tool chain or in your application, the speed of the glob matcher not only impacts your experience, but a slow glob matcher can slow everything else down, limitimg what you thought was possible in your application.
+
+### Braces is fast
+
+> minimatch gets exponentially slower as patterns increase in complexity, braces does not
+
+Brace patterns are meant to be expanded - at least, if you're using Bash, that is. It's not at all unusual for users to want to use brace patterns for dates or similar ranges. It's also very easy to define a brace pattern that appears to be very simple but in fact is fairly complex.
+
+For example, here is how generated regex size and processing time compare as patterns increase in complexity:
+
+_(the following results were generated using `braces()` and `minimatch.braceExpand()`)_
+
+| **Pattern** | **minimatch** | **braces** | 
+| --- | --- | --- |
+| `{1..9007199254740991}`<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> | N/A (freezes) | `300 B` (12ms 878μs) |
+| `{1..10000000}` | `98.9 MB` (20s 193ms 13μs) | `34 B` (11ms 204μs) |
+| `{1..1000000}` | `8.89 MB` (1s 838ms 718μs) | `33 B` (1ms 761μs) |
+| `{1..100000}` | `789 kB` (181ms 518μs) | `32 B` (1ms 76μs) |
+| `{1..10000}` | `68.9 kB` (17ms 436μs) | `31 B` (1ms 382μs) |
+| `{1..1000}` | `5.89 kB` (1ms 773μs) | `30 B` (1ms 509μs) |
+| `{1..100}` | `491 B` (321μs) | `24 B` (309μs) |
+| `{1..10}` | `40 B` (56μs) | `12 B` (333μs) |
+| `{1..3}` | `11 B` (80μs) | `9 B` (370μs) |
+
+These numbers are actually pretty small as far as numeric ranges are concerned. Regardless, we shouldn't have to consider such things when creating a glob pattern. The tool should get out of your way and let you be as creative as you want.
+
+### Braces is performant
+
+Even when brace patterns are fully **expanded**, `braces` is still much faster.
+
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`)_
+
+| **Pattern** | **minimatch** | **braces** | 
+| --- | --- | --- | --- | --- | --- |
+| `a/{1..10000000}` | `98.9 MB` (19s 754ms 376μs) | `98.9 MB` (5s 734ms 419μs) |
+| `a/{1..1000000}` | `8.89 MB` (1s 866ms 968μs) | `8.89 MB` (561ms 594μs) |
+| `a/{1..100000}` | `789 kB` (178ms 311μs) | `789 kB` (29ms 823μs) |
+| `a/{1..10000}` | `68.9 kB` (17ms 692μs) | `68.9 kB` (2ms 351μs) |
+| `a/{1..1000}` | `5.89 kB` (1ms 823μs) | `5.89 kB` (706μs) |
+| `a/{1..100}` | `491 B` (609μs) | `491 B` (267μs) |
+| `a/{1..10}` | `40 B` (61μs) | `40 B` (636μs) |
+| `a/{1..3}` | `11 B` (206μs) | `11 B` (207μs) |
+
+### Why is braces so fast?
+
+Braces was built from the ground up to be as performant as possible when matching, using a combination of the following:
+
+* **minimizes loops and iterating**: we try to pass over the pattern once to parse it before passing it to the compiler.
+* **generates an optimized string**: sequences/ranges are optimized by [to-regex-range](https://github.com/jonschlinkert/to-regex-range), which is not only highly accurate, but produces patterns that are a fraction of the size of patterns generated by other brace expansion libraries, such as Bash and [minimatch](https://github.com/isaacs/minimatch) (via [brace-expansion](https://github.com/juliangruber/brace-expansion))
+* **generates results faster**: can handle even the most complex patterns that cause other implementations like [minimatch](https://github.com/isaacs/minimatch) and Bash to fail.
+
+### Braces is safe
+
+Last, but perhaps most important of all, unlike [brace-expansion](https://github.com/juliangruber/brace-expansion) and [minimatch](https://github.com/isaacs/minimatch), braces will not freeze your application when a user passes a complex brace pattern.
+
+**The trouble with ~~tribbles~~ brace patterns**
+
+There are unlimited scenarios where brace patterns cause the resulting array to grow geometrically. If you define two brace patterns for example, the result is multiplicative if not exponential. For example, given the pattern `{1..1000}`, we would end up with 1 thousand strings. But if two patterns are given, such as `{1...1000}{1...1000}`, we would end up with 1 million strings.
+
+It's easy to think, "I wouldn't do that", but that's not the point. Even though those example patterns are contrived:
+
+1. it's not uncommon for users to define brace patterns that result in similarly huge strings (consider date ranges `1-2016`, for example, and how easy it might be for someone to also add an alphabetical range if searching records of some kind)
+2. we can't control how users define their patterns (and we shouldn't have to care)
+3. even if it's uncommon for user to define unwieldy patterns, there is still potential for intentionally exploiting this feature to [cause a DoS](https://en.wikipedia.org/wiki/Denial-of-service_attack) in your application (and given that this is a not-very-technically-advanced method for causing a DoS - e.g. anyone can do it, you should be concerned!)
+
+**Potential for DoS attacks**
+
+Most brace expansion libraries, including Bash, [minimatch](https://github.com/isaacs/minimatch), [multimatch](https://github.com/sindresorhus/multimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) are vulnerable to DoS attacks (similar to the [ReDoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc) minimatch had recently).
+
+Braces mitigates this risk in three ways:
+
+1. Braces prevents malicious strings from being used by checking the length of the input strings, as well as generated regex
+2. Braces [optimizes](#optimize) the generated result by default, so that only _one output string is generated for every input string_.
+3. When [.expand](#expand) or [options.expand](#optionsexpand) are used, braces has checks in place to prevent huge ranges from being (accidentally) created. By default, when more than 250 strings will result from the given pattern, braces will [optimize](#optimize) the result instead of expanding it. You can override this limit of 250 by passing a custom number on [options.rangeLimit](#optionsrangelimit).
+
+_(you might also want to consider using [micromatch](https://github.com/jonschlinkert/micromatch) for glob matching, as a safer alternative to minimatch and multimatch)_
+
+## About
+
+### Related projects
+
+* [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.")
+* [extglob](https://www.npmjs.com/package/extglob): Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to… [more](https://github.com/jonschlinkert/extglob) | [homepage](https://github.com/jonschlinkert/extglob "Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to glob patterns.")
+* [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.")
+* [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/jonschlinkert/nanomatch) | [homepage](https://github.com/jonschlinkert/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor**<br/> | 
+| --- | --- | --- | --- | --- | --- | --- | --- |
+| 138 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 4 | [doowb](https://github.com/doowb) |
+| 1 | [es128](https://github.com/es128) |
+| 1 | [eush77](https://github.com/eush77) |
+| 1 | [hemanth](https://github.com/hemanth) |
+
+### 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/braces/blob/master/LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on October 21, 2016._
+
+<hr class="footnotes-sep">
+<section class="footnotes">
+<ol class="footnotes-list">
+<li id="fn1"  class="footnote-item">this is the largest safe integer allowed in JavaScript. <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/brace-expansion.js b/benchmark/code/brace-expansion.js
new file mode 100644
index 0000000..8f1599b
--- /dev/null
+++ b/benchmark/code/brace-expansion.js
@@ -0,0 +1,4 @@
+var braceExpansion = require('brace-expansion');
+module.exports = function(args) {
+  return braceExpansion.apply(null, Array.isArray(args) ? args : [args]);
+};
diff --git a/benchmark/code/braces.js b/benchmark/code/braces.js
new file mode 100644
index 0000000..2e93852
--- /dev/null
+++ b/benchmark/code/braces.js
@@ -0,0 +1,4 @@
+var braces = require('../..');
+module.exports = function(args) {
+  return braces.apply(null, Array.isArray(args) ? args : [args]);
+};
diff --git a/benchmark/code/minimatch.js b/benchmark/code/minimatch.js
new file mode 100644
index 0000000..5eea207
--- /dev/null
+++ b/benchmark/code/minimatch.js
@@ -0,0 +1,4 @@
+var braceExpand = require('minimatch').braceExpand;
+module.exports = function(args) {
+  return braceExpand.apply(null, Array.isArray(args) ? args : [args]);
+};
diff --git a/benchmark/fixtures/combination-nested.js b/benchmark/fixtures/combination-nested.js
new file mode 100644
index 0000000..bed3e8d
--- /dev/null
+++ b/benchmark/fixtures/combination-nested.js
@@ -0,0 +1 @@
+module.exports = ['a{b,c{1..100}/{foo/bar},h}x/z'];
diff --git a/benchmark/fixtures/combination.js b/benchmark/fixtures/combination.js
new file mode 100644
index 0000000..699795a
--- /dev/null
+++ b/benchmark/fixtures/combination.js
@@ -0,0 +1 @@
+module.exports = ['a/{b,c,d}/{e,f,g}/h/{1..100}'];
diff --git a/benchmark/fixtures/escaped.js b/benchmark/fixtures/escaped.js
new file mode 100644
index 0000000..b5e69d5
--- /dev/null
+++ b/benchmark/fixtures/escaped.js
@@ -0,0 +1 @@
+module.exports = ['a/\\{b,c}/{x\\,y}/d/e'];
diff --git a/benchmark/fixtures/list-basic.js b/benchmark/fixtures/list-basic.js
new file mode 100644
index 0000000..916273a
--- /dev/null
+++ b/benchmark/fixtures/list-basic.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/{x,y,z}/d/e'];
diff --git a/benchmark/fixtures/list-multiple.js b/benchmark/fixtures/list-multiple.js
new file mode 100644
index 0000000..405aac2
--- /dev/null
+++ b/benchmark/fixtures/list-multiple.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/{k,l,m}/d/{w,x,y,z}/d/e'];
diff --git a/benchmark/fixtures/match.multiple.js b/benchmark/fixtures/match.multiple.js
new file mode 100644
index 0000000..699795a
--- /dev/null
+++ b/benchmark/fixtures/match.multiple.js
@@ -0,0 +1 @@
+module.exports = ['a/{b,c,d}/{e,f,g}/h/{1..100}'];
diff --git a/benchmark/fixtures/match.sequence.js b/benchmark/fixtures/match.sequence.js
new file mode 100644
index 0000000..e94a78f
--- /dev/null
+++ b/benchmark/fixtures/match.sequence.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/{1..100}/d/e'];
diff --git a/benchmark/fixtures/no-braces.js b/benchmark/fixtures/no-braces.js
new file mode 100644
index 0000000..3b4653e
--- /dev/null
+++ b/benchmark/fixtures/no-braces.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/d/e/**/@(x|y|z)*.js'];
diff --git a/benchmark/fixtures/sequence-basic.js b/benchmark/fixtures/sequence-basic.js
new file mode 100644
index 0000000..e94a78f
--- /dev/null
+++ b/benchmark/fixtures/sequence-basic.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/{1..100}/d/e'];
diff --git a/benchmark/fixtures/sequence-multiple.js b/benchmark/fixtures/sequence-multiple.js
new file mode 100644
index 0000000..ba55e69
--- /dev/null
+++ b/benchmark/fixtures/sequence-multiple.js
@@ -0,0 +1 @@
+module.exports = ['a/b/c/{1..50}/d/{1..100}/d/e'];
diff --git a/benchmark/index.js b/benchmark/index.js
new file mode 100644
index 0000000..23b4d80
--- /dev/null
+++ b/benchmark/index.js
@@ -0,0 +1,30 @@
+'use strict';
+
+var path = require('path');
+var util = require('util');
+var cyan = require('ansi-cyan');
+var argv = require('yargs-parser')(process.argv.slice(2));
+var Suite = require('benchmarked');
+
+function run(type, fixtures) {
+  var suite = new Suite({
+    cwd: __dirname,
+    fixtures: `fixtures/${fixtures}.js`,
+    code: `code/${type}.js`
+  });
+
+  if (argv.dry) {
+    suite.dryRun(function(code, fixture) {
+      console.log(cyan('%s > %s'), code.key, fixture.key);
+      var args = require(fixture.path);
+      var res = code.run(args);
+      console.log(util.inspect(res, null, 10));
+      console.log();
+    });
+  } else {
+    suite.run();
+  }
+}
+
+run(argv.code || '*', argv._[0] || '!(match)*');
+// run('braces', 'no-*');
diff --git a/benchmark/last.md b/benchmark/last.md
new file mode 100644
index 0000000..2297e01
--- /dev/null
+++ b/benchmark/last.md
@@ -0,0 +1,65 @@
+Benchmarking: (8 of 8)
+ · combination-nested
+ · combination
+ · escaped
+ · list-basic
+ · list-multiple
+ · no-braces
+ · sequence-basic
+ · sequence-multiple
+
+# benchmark/fixtures/combination-nested.js (52 bytes)
+  brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
+  braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
+  minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/combination.js (51 bytes)
+  brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
+  braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
+  minimatch x 637 ops/sec ±0.84% (88 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/escaped.js (44 bytes)
+  brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
+  braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
+  minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/list-basic.js (40 bytes)
+  brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
+  braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
+  minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/list-multiple.js (52 bytes)
+  brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
+  braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
+  minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/no-braces.js (48 bytes)
+  brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
+  braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
+  minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/sequence-basic.js (41 bytes)
+  brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
+  braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
+  minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
+
+  fastest is braces
+
+# benchmark/fixtures/sequence-multiple.js (51 bytes)
+  brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
+  braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
+  minimatch x 109 ops/sec ±1.16% (76 runs sampled)
+
+  fastest is braces
diff --git a/examples.md b/examples.md
new file mode 100644
index 0000000..9a90d3a
--- /dev/null
+++ b/examples.md
@@ -0,0 +1,60 @@
+# Examples
+
+> Usage examples
+
+TBC...
+
+## Sequences
+
+Letters
+
+```js
+'{a..c}'
+//=> ['a', 'b', 'c']
+```
+
+Numbers
+
+```js
+'{1..3}'
+//=> ['1', '2', '3']
+```
+
+### Sequences with steps
+
+Letters with step:
+
+```js
+'{a..j..2}'
+//=> ['b', 'd', 'f', 'h', 'j']
+```
+
+Numbers with step:
+
+```js
+'{1..10..2}'
+//=> ['2', '4', '6', '8', '10']
+```
+
+## Lists
+
+```js
+'{a,b}'
+//=> ['a', 'b']
+
+'a/{b,c}'
+//=> ['a/b', 'a/c']
+```
+
+## Nesting
+
+```js
+'a/{b,c/{d,e}}/f'
+//=> ['a/b/f', 'a/c/d/f', 'a/c/e/f']
+
+'a/{b,c/{d,e}/f,g}/h'
+//=> ['a/b/h', 'a/c/d/f/h', 'a/c/e/f/h', 'a/g/h']
+
+'a/{b,c/{2..8..2}}/f'
+//=> ['a/b/f', 'a/c/2/f', 'a/c/4/f', 'a/c/6/f', 'a/c/8/f']
+```
diff --git a/examples/comparison.js b/examples/comparison.js
new file mode 100644
index 0000000..5738d15
--- /dev/null
+++ b/examples/comparison.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var braceExpansion = require('brace-expansion');
+var braces = require('..');
+
+console.log('braces');
+console.log(braces('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html'));
+console.log(braces('http://www.numericals.com/file{1..100..10}.txt'));
+console.log(braces('http://www.letters.com/file{a..z..2}.txt'));
+console.log(braces('mkdir /usr/local/src/bash/{old,new,dist,bugs}'));
+console.log(braces('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}'));
+console.log();
+console.log();
+console.log('braces: {expand: true}');
+console.log(braces('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html', {expand: true}));
+console.log(braces('http://www.numericals.com/file{1..100..10}.txt', {expand: true}));
+console.log(braces('http://www.letters.com/file{a..z..2}.txt', {expand: true}));
+console.log(braces('mkdir /usr/local/src/bash/{old,new,dist,bugs}', {expand: true}));
+console.log(braces('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}', {expand: true}));
+console.log();
+console.log();
+console.log('brace-expansion');
+console.log(braceExpansion('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html'));
+console.log(braceExpansion('http://www.numericals.com/file{1..100..10}.txt'));
+console.log(braceExpansion('http://www.letters.com/file{a..z..2}.txt'));
+console.log(braceExpansion('mkdir /usr/local/src/bash/{old,new,dist,bugs}'));
+console.log(braceExpansion('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}'));
+
+
+console.log(braces('user-{200..300}/project-{a,b,c}-{1..10}'))
+//=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
+console.log(braces.makeRe('user-{200..300}'))
+//=> /^(?:user-(20[0-9]|2[1-9][0-9]|300))$/
diff --git a/examples/escape.js b/examples/escape.js
new file mode 100644
index 0000000..c597ec2
--- /dev/null
+++ b/examples/escape.js
@@ -0,0 +1,4 @@
+'use strict';
+
+var braces = require('..');
+console.log(braces.expand('{1\\.2}'));
diff --git a/examples/expand.js b/examples/expand.js
new file mode 100644
index 0000000..e3a9af8
--- /dev/null
+++ b/examples/expand.js
@@ -0,0 +1,7 @@
+'use strict';
+
+var mm = require('minimatch');
+var braces = require('..');
+
+console.log(braces.expand('a/{b,c}/d'));
+//=> [ 'a/b/d', 'a/c/d' ]
diff --git a/examples/nested.js b/examples/nested.js
new file mode 100644
index 0000000..6d6d532
--- /dev/null
+++ b/examples/nested.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var minimatch = require('minimatch');
+var braces = require('..');
+
+console.log('braces');
+console.log(braces.makeRe('a{b,c{1..100}/{foo/bar},h}x/z'));
+console.log();
+console.log();
+console.log('brace-expansion');
+console.log(minimatch.makeRe('a{b,c{1..100}/{foo/bar},h}x/z'));
+
diff --git a/examples/options.quantifiers.js b/examples/options.quantifiers.js
new file mode 100644
index 0000000..72c55e3
--- /dev/null
+++ b/examples/options.quantifiers.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var mm = require('minimatch');
+var braces = require('..');
+
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
diff --git a/examples/regex.js b/examples/regex.js
new file mode 100644
index 0000000..3b3b8fa
--- /dev/null
+++ b/examples/regex.js
@@ -0,0 +1,17 @@
+'use strict';
+
+var mm = require('minimatch');
+var braces = require('..');
+
+// console.log(braces.makeRe('a/b/c/{k,l,m}/d/{w,x,y,z}/d/e', {expand: true}));
+// console.log(mm.makeRe('a/b/c/{k,l,m}/d/{w,x,y,z}/d/e'));
+
+// console.log('braces.makeRe: "foo/{1..20000}/bar/{a..j}/baz"');
+// console.log(braces.makeRe('foo/{1..20000}/bar/{a..j}/baz'));
+// console.log();
+// console.log('minimatch.makeRe: "foo/{1..20000}/bar/{a..j}/baz"');
+// console.log(mm.makeRe('foo/{1..20000}/bar/{a..j}/baz'));
+
+var re = braces.makeRe('a/{foo/bar}/z');
+console.log(re);
+console.log(re.test('a/{foo/bar}/z'));
diff --git a/examples/sequences-steps.js b/examples/sequences-steps.js
new file mode 100644
index 0000000..6895aa9
--- /dev/null
+++ b/examples/sequences-steps.js
@@ -0,0 +1,7 @@
+'use strict';
+
+var mm = require('minimatch');
+var braces = require('..');
+
+console.log(braces('{0..10..2}', {expand: true}));
+//=> [ '0', '2', '4', '6', '8', '10' ]
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..0f8b221
--- /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('eslint', function() {
+  return gulp.src(['*.js', 'lib/*.js', 'test/*.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', 'eslint']);
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..a6f6b4e
--- /dev/null
+++ b/index.js
@@ -0,0 +1,300 @@
+'use strict';
+
+/**
+ * Module dependencies
+ */
+
+var toRegex = require('to-regex');
+var unique = require('array-unique');
+var extend = require('extend-shallow');
+var debug = require('debug')('braces');
+
+/**
+ * Local dependencies
+ */
+
+var compilers = require('./lib/compilers');
+var parsers = require('./lib/parsers');
+var Braces = require('./lib/braces');
+var utils = require('./lib/utils');
+var MAX_LENGTH = 1024 * 64;
+
+/**
+ * Session cache (disable with `options.cache`)
+ */
+
+var cache = {};
+
+/**
+ * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)).
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces('{a,b,c}'));
+ * //=> ['(a|b|c)']
+ *
+ * console.log(braces('{a,b,c}', {expand: true}));
+ * //=> ['a', 'b', 'c']
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+function braces(pattern, options) {
+  debug('initializing from <%s>', __filename);
+
+  var key = utils.createKey(pattern, options);
+  options = options || {};
+
+  if (options.cache !== false && cache.hasOwnProperty(key)) {
+    return cache[key];
+  }
+
+  var results = [];
+  if (Array.isArray(pattern)) {
+    for (var i = 0; i < pattern.length; i++) {
+      results.push.apply(results, braces.create(pattern[i], options));
+    }
+
+    if (options && options.nodupes === true) {
+      results = unique(results);
+    }
+  } else {
+    results = braces.create(pattern, options);
+  }
+
+  return (cache[key] = results);
+}
+
+/**
+ * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/b/d', 'a/c/d'];
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.expand = function(pattern, options) {
+  return braces.create(pattern, extend({}, options, {expand: true}));
+};
+
+/**
+ * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/(b|c)/d']
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.optimize = function(pattern, options) {
+  return braces.create(pattern, options);
+};
+
+/**
+ * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
+ * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.create = function(pattern, options) {
+  if (pattern instanceof RegExp) {
+    pattern = pattern.source;
+  }
+
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected a string');
+  }
+
+  if (pattern.length > MAX_LENGTH) {
+    throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
+  }
+
+  function create() {
+    if (pattern === '' || pattern.length <= 2) {
+      return [pattern];
+    }
+
+    if (/^(?:{,})+$/.test(pattern)) {
+      return [];
+    }
+
+    var quoted = /^(['"`])(.*)(\1)$/g.exec(pattern);
+    if (quoted) {
+      return [quoted[2]];
+    }
+
+    var proto = new Braces(options);
+    var arr = !options || !options.expand
+      ? proto.optimize(pattern, options)
+      : proto.expand(pattern, options);
+
+    if (options && options.nodupes === true) {
+      return unique(arr);
+    }
+
+    return arr;
+  }
+
+  return memoize('create', pattern, options, create);
+};
+
+/**
+ * Create a regular expression from the given string `pattern`.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.makeRe('user-{200..300}'))
+ * //=> /^(?:user-(20[0-9]|2[1-9][0-9]|300))$/
+ * ```
+ * @param {String} `pattern` The pattern to convert to regex.
+ * @param {Object} `options`
+ * @return {RegExp}
+ * @api public
+ */
+
+braces.makeRe = function(pattern, options) {
+  if (pattern instanceof RegExp) {
+    return pattern;
+  }
+
+  if (typeof pattern !== 'string') {
+    throw new TypeError('expected a string');
+  }
+
+  if (pattern.length > MAX_LENGTH) {
+    throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
+  }
+
+  function makeRe() {
+    var arr = braces(pattern, options);
+    for (var i = 0; i < arr.length; i++) {
+      arr[i] = arr[i].replace(/([{}])/g, '\\$1');
+    }
+    var opts = extend({strictErrors: false}, options);
+    return toRegex(arr, opts);
+  }
+
+  return memoize('makeRe', pattern, options, makeRe);
+};
+
+/**
+ * Parse the given `str` with the given `options`.
+ *
+ * ```js
+ * var braces = require('braces');
+ * var ast = braces.parse('a/{b,c}/d');
+ * console.log(ast);
+ * // { type: 'root',
+ * //   errors: [],
+ * //   input: 'a/{b,c}/d',
+ * //   nodes:
+ * //    [ { type: 'bos', val: '' },
+ * //      { type: 'text', val: 'a/' },
+ * //      { type: 'brace',
+ * //        nodes:
+ * //         [ { type: 'brace.open', val: '{' },
+ * //           { type: 'text', val: 'b,c' },
+ * //           { type: 'brace.close', val: '}' } ] },
+ * //      { type: 'text', val: '/d' },
+ * //      { type: 'eos', val: '' } ] }
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {Object} Returns an AST
+ * @api public
+ */
+
+braces.parse = function(str, options) {
+  var inst = new Braces(options);
+  return inst.parse(str, options);
+};
+
+/**
+ * Compile the given `ast` or string with the given `options`.
+ *
+ * ```js
+ * var braces = require('braces');
+ * var ast = braces.parse('a/{b,c}/d');
+ * console.log(braces.compile(ast));
+ * // { options: { source: 'string' },
+ * //   state: {},
+ * //   compilers:
+ * //    { eos: [Function],
+ * //      noop: [Function],
+ * //      bos: [Function],
+ * //      brace: [Function],
+ * //      'brace.open': [Function],
+ * //      text: [Function],
+ * //      'brace.close': [Function] },
+ * //   output: [ 'a/(b|c)/d' ],
+ * //   ast:
+ * //    { ... },
+ * //   parsingErrors: [] }
+ * ```
+ * @param {Object|String} `ast`
+ * @param {Object} `options`
+ * @return {Object} Returns an object that has an `output` property with the compiled string.
+ * @api public
+ */
+
+braces.compile = function(ast, options) {
+  var inst = new Braces(options);
+  return inst.compile(ast, options);
+};
+
+/**
+ * Memoize if `options.cache` is not false
+ */
+
+function memoize(type, pattern, options, fn) {
+  var key = utils.createKey(type + ':' + pattern, options);
+  options = options || {};
+
+  if (options.cache !== false && cache.hasOwnProperty(key)) {
+    return cache[key];
+  }
+
+  var res = fn(pattern, options);
+  if (options.cache !== false) {
+    cache[key] = res;
+  }
+  return res;
+}
+
+/**
+ * Expose `braces`
+ * @type {Function}
+ */
+
+module.exports = braces;
+
+/**
+ * Expose `Braces` constructor
+ * @type {Function}
+ */
+
+module.exports.Braces = Braces;
+module.exports.compilers = compilers;
+module.exports.parsers = parsers;
diff --git a/lib/braces.js b/lib/braces.js
new file mode 100644
index 0000000..0c58873
--- /dev/null
+++ b/lib/braces.js
@@ -0,0 +1,133 @@
+'use strict';
+
+var debug = require('debug')('braces');
+var Snapdragon = require('snapdragon');
+var compilers = require('./compilers');
+var parsers = require('./parsers');
+var utils = require('./utils');
+
+/**
+ * Customize Snapdragon parser and renderer
+ */
+
+function Braces(options) {
+  debug('initializing from <%s>', __filename);
+  this.options = utils.extend({}, options);
+}
+
+/**
+ * Initialize braces
+ */
+
+Braces.prototype.init = function(options) {
+  var opts = utils.createOptions({}, this.options, options);
+  this.snapdragon = this.options.snapdragon || new Snapdragon(opts);
+  this.compiler = this.snapdragon.compiler;
+  this.parser = this.snapdragon.parser;
+
+  compilers(this.snapdragon);
+  parsers(this.snapdragon);
+
+  /**
+   * Call Snapdragon `.parse` method. When AST is returned, we check to
+   * see if any unclosed braces are left on the stack and, if so, we iterate
+   * over the stack and correct the AST so that compilers are called in the correct
+   * order and unbalance braces are properly escaped.
+   */
+
+  utils.define(this.snapdragon, 'parse', function(pattern, options) {
+    var parsed = Snapdragon.prototype.parse.apply(this, arguments);
+    this.parser.ast.input = pattern;
+
+    var stack = this.parser.stack;
+    while (stack.length) {
+      addParent({type: 'brace.close', val: ''}, stack.pop());
+    }
+
+    function addParent(node, parent) {
+      utils.define(node, 'parent', parent);
+      parent.nodes.push(node);
+    }
+
+    // add non-enumerable parser reference
+    utils.define(parsed, 'parser', this.parser);
+    return parsed;
+  });
+};
+
+/**
+ * Lazily initialize braces
+ */
+
+Braces.prototype.lazyInit = function(options) {
+  if (!this.isInitialized) {
+    this.isInitialized = true;
+    this.init(options);
+  }
+};
+
+/**
+ * Decorate `.parse` method
+ */
+
+Braces.prototype.parse = function(ast, options) {
+  if (utils.isObject(ast) && ast.nodes) return ast;
+  this.lazyInit(options);
+  return this.snapdragon.parse(ast, options);
+};
+
+/**
+ * Decorate `.compile` method
+ */
+
+Braces.prototype.compile = function(ast, options) {
+  if (typeof ast === 'string') ast = this.parse(ast, options);
+  this.lazyInit(options);
+  return this.snapdragon.compile(ast, options);
+};
+
+/**
+ * Expand
+ */
+
+Braces.prototype.expand = function(pattern) {
+  var ast = this.parse(pattern, {expand: true});
+  var res = this.compile(ast, {expand: true});
+  return res.output;
+};
+
+/**
+ * Optimize
+ */
+
+Braces.prototype.optimize = function(pattern) {
+  var ast = this.parse(pattern, {optimize: true});
+  var res = this.compile(ast, {optimize: true});
+  return res.output;
+};
+
+/**
+ * Visit `node` with the given `fn`
+ */
+
+function visit(node, fn) {
+  return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
+}
+
+/**
+ * Map visit over array of `nodes`.
+ */
+
+function mapVisit(nodes, fn) {
+  var len = nodes.length;
+  var idx = -1;
+  while (++idx < len) {
+    visit(nodes[idx], fn);
+  }
+}
+
+/**
+ * Expose `Braces`
+ */
+
+module.exports = Braces;
diff --git a/lib/compilers.js b/lib/compilers.js
new file mode 100644
index 0000000..46bfdc3
--- /dev/null
+++ b/lib/compilers.js
@@ -0,0 +1,270 @@
+'use strict';
+
+var utils = require('./utils');
+
+module.exports = function(braces) {
+  braces.compiler
+
+    /**
+     * bos
+     */
+
+    .set('bos', function() {
+      this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : [];
+      this.ast.count = 1;
+    })
+
+    /**
+     * Square brackets
+     */
+
+    .set('bracket', function(node) {
+      var close = node.close;
+      var open = !node.escaped ? '[' : '\\[';
+      var negated = node.negated;
+      var inner = node.inner;
+
+      inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\');
+      if (inner === ']-') {
+        inner = '\\]\\-';
+      }
+
+      if (negated && inner.indexOf('.') === -1) {
+        inner += '.';
+      }
+      if (negated && inner.indexOf('/') === -1) {
+        inner += '/';
+      }
+
+      var val = open + negated + inner + close;
+      var queue = node.parent.queue;
+      var last = utils.arrayify(queue.pop());
+
+      queue.push(utils.join(last, val));
+      queue.push.apply(queue, []);
+    })
+
+    /**
+     * Brace
+     */
+
+    .set('brace', function(node) {
+      node.queue = isEscaped(node) ? [node.val] : [];
+      node.count = 1;
+      return this.mapVisit(node.nodes);
+    })
+
+    /**
+     * Open
+     */
+
+    .set('brace.open', function(node) {
+      node.parent.open = node.val;
+    })
+
+    /**
+     * Inner
+     */
+
+    .set('text', function(node) {
+      var queue = node.parent.queue;
+      var segs = [node.val];
+      var escaped = node.escaped;
+
+      if (node.multiplier > 1) {
+        node.parent.count *= node.multiplier;
+      }
+
+      if (this.options.quantifiers === true && /^(\d,\d|,\d|\d,)$/.test(node.val)) {
+        escaped = true;
+
+      } else if (node.val.length > 1) {
+        if (isType(node.parent, 'brace') && !isEscaped(node)) {
+          var expanded = utils.expand(node.val, this.options);
+          segs = expanded.segs;
+
+          if (expanded.isOptimized) {
+            node.parent.isOptimized = true;
+          }
+
+          // if nothing was expanded, we probably have a literal brace
+          if (!segs.length) {
+            var val = (expanded.val || node.val)
+              // unescape unexpanded brace sequence/set separators
+              .replace(/\\([,.])/g, '$1')
+              // strip quotes
+              .replace(/["']/g, '');
+
+            segs = [val];
+            escaped = true;
+          }
+        }
+
+      } else if (node.val === ',') {
+        if (this.options.expand) {
+          node.parent.queue.push([]);
+          segs = [''];
+        } else {
+          segs = ['|'];
+        }
+      } else {
+        escaped = true;
+      }
+
+      if (escaped && isType(node.parent, 'brace')) {
+        if (node.parent.nodes.length <= 4 && node.parent.count === 1) {
+          node.parent.escaped = true;
+        } else if (node.parent.length <= 3) {
+          node.parent.escaped = true;
+        }
+      }
+
+      if (!hasQueue(node.parent)) {
+        node.parent.queue = segs;
+        return;
+      }
+
+      var last = utils.arrayify(queue.pop());
+      if (node.parent.count > 1 && this.options.expand) {
+        last = multiply(last, node.parent.count);
+        node.parent.count = 1;
+      }
+
+      var temp = utils.join(utils.flatten(last), segs.shift());
+      queue.push(temp);
+      queue.push.apply(queue, segs);
+    })
+
+    /**
+     * Close
+     */
+
+    .set('brace.close', function(node) {
+      var queue = node.parent.queue;
+      var prev = node.parent.parent;
+      var last = prev.queue.pop();
+      var open = node.parent.open;
+      var close = node.val;
+
+      if (open && close && isOptimized(node, this.options)) {
+        open = '(';
+        close = ')';
+      }
+
+      // if a close brace exists, and the previous segment is one character
+      // don't wrap the result in braces or parens
+      var ele = utils.last(queue);
+      if (node.parent.count > 1 && this.options.expand) {
+        ele = multiply(queue.pop(), node.parent.count);
+        node.parent.count = 1;
+        queue.push(ele);
+      }
+
+      if (close && typeof ele === 'string' && ele.length === 1) {
+        open = '';
+        close = '';
+      }
+
+      if (isLiteralBrace(node, this.options) || noInner(node)) {
+        queue.push(utils.join(open, queue.pop() || ''));
+        queue = utils.flatten(utils.join(queue, close));
+      }
+
+      var arr = utils.flatten(utils.join(last, queue));
+      prev.queue.push(arr);
+    })
+
+    /**
+     * eos
+     */
+
+    .set('eos', function(node) {
+      if (this.options.optimize !== false) {
+        this.output = utils.last(utils.flatten(this.ast.queue));
+      } else if (Array.isArray(utils.last(this.ast.queue))) {
+        this.output = utils.flatten(this.ast.queue.pop());
+      } else {
+        this.output = utils.flatten(this.ast.queue);
+      }
+
+      if (node.parent.count > 1 && this.options.expand) {
+        this.output = multiply(this.output, node.parent.count);
+      }
+
+      this.output = utils.arrayify(this.output);
+    });
+
+};
+
+/**
+ * Multiply the segments in the current brace level
+ */
+
+function multiply(queue, n, options) {
+  return utils.flatten(utils.repeat(utils.arrayify(queue), n));
+}
+
+/**
+ * Return true if `node` is escaped
+ */
+
+function isEscaped(node) {
+  return node.escaped === true;
+}
+
+/**
+ * Returns true if regex parens should be used for sets. If the parent `type`
+ * is not `brace`, then we're on a root node, which means we should never
+ * expand segments and open/close braces should be `{}` (since this indicates
+ * a brace is missing from the set)
+ */
+
+function isOptimized(node, options) {
+  if (node.parent.isOptimized) return true;
+  return isType(node.parent, 'brace')
+    && !isEscaped(node.parent)
+    && options.expand !== true;
+}
+
+/**
+ * Returns true if the value in `node` should be wrapped in a literal brace.
+ * @return {Boolean}
+ */
+
+function isLiteralBrace(node, options) {
+  return isEscaped(node.parent) || options.optimize !== false;
+}
+
+/**
+ * Returns true if the given `node` does not have an inner value.
+ * @return {Boolean}
+ */
+
+function noInner(node, type) {
+  if (node.parent.queue.length === 1) {
+    return true;
+  }
+  var nodes = node.parent.nodes;
+  return nodes.length === 3
+    && isType(nodes[0], 'brace.open')
+    && !isType(nodes[1], 'text')
+    && isType(nodes[2], 'brace.close');
+}
+
+/**
+ * Returns true if the given `node` is the given `type`
+ * @return {Boolean}
+ */
+
+function isType(node, type) {
+  return typeof node !== 'undefined' && node.type === type;
+}
+
+/**
+ * Returns true if the given `node` has a non-empty queue.
+ * @return {Boolean}
+ */
+
+function hasQueue(node) {
+  return Array.isArray(node.queue) && node.queue.length;
+}
diff --git a/lib/parsers.js b/lib/parsers.js
new file mode 100644
index 0000000..ce756ba
--- /dev/null
+++ b/lib/parsers.js
@@ -0,0 +1,238 @@
+'use strict';
+
+var utils = require('./utils');
+var regexNot = require('regex-not');
+var toRegex = require('to-regex');
+
+/**
+ * Characters to use in negation regex (we want to "not" match
+ * characters that are matched by other parsers)
+ */
+
+var cached;
+var regex = textRegex();
+
+/**
+ * Braces parsers
+ */
+
+module.exports = function(brace) {
+  brace.parser.sets.brace = brace.parser.sets.brace || [];
+  brace.parser
+
+    /**
+     * Brackets: "[...]" (basic, this can be overridden by other parsers)
+     */
+
+    .capture('bracket', function() {
+      var pos = this.position();
+      var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]\-)(\]|[^*+?]+)|\[)/);
+      if (!m) return;
+
+      var val = m[0];
+      var negated = m[1] ? '^' : '';
+      var inner = m[2] || '';
+      var close = m[3] || '';
+
+      var esc = this.input.slice(0, 2);
+      if (inner === '' && esc === '\\]') {
+        inner += esc;
+        this.consume(2);
+
+        var str = this.input;
+        var idx = -1;
+        var ch;
+
+        while ((ch = str[++idx])) {
+          this.consume(1);
+          if (ch === ']') {
+            close = ch;
+            break;
+          }
+          inner += ch;
+        }
+      }
+
+      return pos({
+        type: 'bracket',
+        val: val,
+        escaped: close !== ']',
+        negated: negated,
+        inner: inner,
+        close: close
+      });
+    })
+
+    /**
+     * Character parsers
+     */
+
+    .capture('escape', function() {
+      var pos = this.position();
+      var m = this.match(/^(?:\\(.)|\$\{)/);
+      if (!m) return;
+
+      var node = pos({
+        type: 'text',
+        multiplier: 1,
+        val: m[0]
+      });
+
+      if (node.val === '\\\\') {
+        return node;
+      }
+
+      var chars = {'${': '}', '`': '`', '"': '"'};
+      var val = node.val;
+      if (chars[val]) {
+        var str = this.input;
+        var idx = -1;
+        var ch;
+
+        while ((ch = str[++idx])) {
+          this.consume(1);
+          node.val += ch;
+          if (ch === '\\') {
+            ch += str[++idx];
+            node.val += ch;
+          }
+          if (ch === chars[val]) {
+            break;
+          }
+        }
+      }
+
+      if (this.options.unescape !== false) {
+        node.val = node.val.replace(/\\([{}])/g, '$1');
+      }
+
+      return concatNodes.call(this, pos, node);
+    })
+
+    /**
+     * Open
+     */
+
+    .capture('brace.open', function() {
+      var pos = this.position();
+      var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/);
+      if (!m) return;
+
+      var prev = this.prev();
+      var val = m[0];
+
+      var open = pos({
+        type: 'brace.open',
+        val: val
+      });
+
+      var node = pos({
+        type: 'brace',
+        nodes: [open]
+      });
+
+      utils.define(node, 'parent', prev);
+      utils.define(open, 'parent', node);
+      this.push('brace', node);
+      prev.nodes.push(node);
+    })
+
+    /**
+     * Close
+     */
+
+    .capture('brace.close', function() {
+      var pos = this.position();
+      var m = this.match(/^\}/);
+      if (!m || !m[0]) return;
+
+      var brace = this.pop('brace');
+      var node = pos({
+        type: 'brace.close',
+        val: m[0]
+      });
+
+      if (!this.isType(brace, 'brace')) {
+        if (this.options.strict) {
+          throw new Error('missing opening "{"');
+        }
+        node.type = 'text';
+        node.multiplier = 1;
+        node.escaped = true;
+        return node;
+      }
+
+      brace.nodes.push(node);
+      utils.define(node, 'parent', brace);
+    })
+
+    /**
+     * Inner
+     */
+
+    .capture('text', function() {
+      var pos = this.position();
+      var m = this.match(regex);
+      if (!m) return;
+
+      var node = pos({
+        type: 'text',
+        multiplier: 1,
+        val: m[0]
+      });
+
+      return concatNodes.call(this, pos, node);
+    });
+
+};
+
+/**
+ * Combine text nodes, and calculate empty sets (`{,,}`)
+ * @param {Function} `pos` Function to calculate node position
+ * @param {Object} `node` AST node
+ * @return {Object}
+ */
+
+function concatNodes(pos, node) {
+  var prev = this.prev();
+  var last = utils.last(prev.nodes);
+  var re = /^\{(,+)\}/;
+  var multi;
+
+  var a = node.val.charAt(0);
+  var b = node.val.slice(-1);
+
+  if ((a === '"' && b === '"') || (a === '`' && b === '`') || (a === "'" && b === "'")) {
+    node.val = node.val.slice(1, node.val.length - 1);
+    node.escaped = true;
+  }
+
+  if (re.test(node.val)) {
+    this.input = node.val + this.input;
+    node.val = '';
+  }
+
+  while ((multi = re.exec(this.input))) {
+    this.consume(multi[0].length);
+    node.multiplier *= multi[1].length + 1;
+  }
+
+  if (last.type === 'text' && node.val && node.multiplier === 1 && last.multiplier === 1) {
+    last.val += node.val;
+    return;
+  }
+
+  return node;
+}
+
+/**
+ * Create and cache regex to use for text nodes
+ */
+
+function textRegex(pattern) {
+  if (cached) return cached;
+  var opts = {contains: true, strictClose: false};
+  var not = regexNot.create('(\\$?\\{|\\\\.|\\}|[\\[\\]])', opts);
+  var re = toRegex('^(?:\\{(,+|.?)\\}|' + not + ')', opts);
+  return (cached = re);
+}
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..36eba65
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,253 @@
+'use strict';
+
+var utils = module.exports;
+
+/**
+ * Module dependencies
+ */
+
+utils.define = require('define-property');
+utils.extend = require('extend-shallow');
+utils.flatten = require('arr-flatten');
+utils.isObject = require('isobject');
+utils.range = require('fill-range');
+utils.repeat = require('repeat-element');
+utils.unique = require('array-unique');
+
+/**
+ * Create the key to use for memoization. The unique 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) {
+    if (options.hasOwnProperty(prop)) {
+      key += ';' + prop + '=' + String(options[prop]);
+    }
+  }
+  return key;
+};
+
+/**
+ * Normalize options
+ */
+
+utils.createOptions = function(options) {
+  var opts = utils.extend.apply(null, arguments);
+  if (typeof opts.expand === 'boolean') {
+    opts.optimize = !opts.expand;
+  }
+  if (typeof opts.optimize === 'boolean') {
+    opts.expand = !opts.optimize;
+  }
+  if (opts.optimize === true) {
+    opts.makeRe = true;
+  }
+  return opts;
+};
+
+/**
+ * Join patterns in `a` to patterns in `b`
+ */
+
+utils.join = function(a, b, options) {
+  options = options || {};
+  a = utils.arrayify(a);
+  b = utils.arrayify(b);
+  if (!a.length) return b;
+  if (!b.length) return a;
+
+  var len = a.length;
+  var idx = -1;
+  var arr = [];
+
+  while (++idx < len) {
+    var str = a[idx];
+    if (Array.isArray(str)) {
+      str = str.map(function(ele) {
+        return utils.join(ele, b);
+      });
+      arr.push(str);
+      continue;
+    }
+
+    for (var i = 0; i < b.length; i++) {
+      if (Array.isArray(b[i])) {
+        arr.push(utils.join(str, b[i]));
+      } else {
+        arr.push(str + b[i]);
+      }
+    }
+  }
+  return arr;
+};
+
+/**
+ * Split the given string on `,` if not escaped.
+ */
+
+utils.split = function(str) {
+  var segs = str.split(',');
+  var tok = {rest: ''};
+  var res = [];
+
+  while (segs.length) {
+    var key = segs.shift();
+    var next = segs[segs.length - 1];
+    var quoted;
+
+    while (key.slice(-1) === '\\' || (quoted = isQuote(key, next))) {
+      if (quoted) tok.quoted = true;
+      tok.escaped = true;
+      var ch = segs.shift();
+      if (!ch) break;
+      key = key.slice(0, -1) + ',' + (quoted ? ch.slice(1) : ch);
+    }
+    res.push(key);
+  }
+
+  if (tok.quoted) {
+    tok.val = res[0];
+  }
+
+  tok.segs = res;
+  return tok;
+};
+
+function isDoubleQuote(key, next) {
+  return key.slice(-1) === '"' && next.charAt(0) === '"';
+}
+
+function isSingleQuote(key, next) {
+  return key.slice(-1) === "'" && next.charAt(0) === "'";
+}
+
+function isQuote(key, next) {
+  return isDoubleQuote(key, next) || isSingleQuote(key, next);
+}
+
+/**
+ * Expand ranges or sets in the given `pattern`.
+ *
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {Object}
+ */
+
+utils.expand = function(str, options) {
+  var opts = utils.extend({rangeLimit: 250}, options);
+  var tok = utils.split(str);
+  var segs = tok.segs;
+
+  if (segs.length > 1) {
+    if (opts.optimize === false) {
+      tok.val = segs[0];
+      return tok;
+    }
+
+    tok.segs = utils.stringify(tok.segs);
+  } else if (segs.length === 1) {
+    var arr = str.split('..');
+
+    if (arr.length === 1) {
+      tok.val = tok.segs.pop() || tok.val || str;
+      tok.segs = [];
+      return tok;
+    }
+
+    if (arr.length === 2 && arr[0] === arr[1]) {
+      tok.escaped = true;
+      tok.val = arr[0];
+      tok.segs = [];
+      return tok;
+    }
+
+    if (arr.length > 1) {
+      if (opts.optimize !== false) {
+        opts.optimize = true;
+        delete opts.expand;
+      }
+
+      if (opts.optimize !== true) {
+        var min = Math.min(arr[0], arr[1]);
+        var max = Math.max(arr[0], arr[1]);
+        var step = arr[2] || 1;
+        if (((max - min) / step) >= opts.rangeLimit) {
+          opts.optimize = true;
+          tok.isOptimized = true;
+          delete opts.expand;
+        }
+      }
+
+      arr.push(opts);
+      tok.segs = utils.range.apply(null, arr);
+
+      if (!tok.segs.length) {
+        tok.escaped = true;
+        tok.val = str;
+        return tok;
+      }
+
+      if (opts.optimize === true) {
+        tok.segs = utils.stringify(tok.segs);
+      }
+
+      if (tok.segs === '') {
+        tok.val = str;
+      } else {
+        tok.val = tok.segs[0];
+      }
+      return tok;
+    }
+  } else {
+    tok.val = str;
+  }
+  return tok;
+};
+
+/**
+ * Cast `val` to an array.
+ * @param {*} `val`
+ */
+
+utils.stringify = function(arr) {
+  return [utils.arrayify(arr).join('|')];
+};
+
+/**
+ * Cast `val` to an array.
+ * @param {*} `val`
+ */
+
+utils.arrayify = function(arr) {
+  if (typeof arr === 'undefined') return [];
+  return Array.isArray(arr) ? arr : [arr];
+};
+
+/**
+ * Returns true if the given `str` is a non-empty string
+ * @return {Boolean}
+ */
+
+utils.isString = function(str) {
+  return str != null && typeof str === 'string';
+};
+
+/**
+ * Get the last element from `array`
+ * @param {Array} `array`
+ * @return {*}
+ */
+
+utils.last = function(arr) {
+  return arr[arr.length - 1];
+};
+
+utils.escapeRegex = function(str) {
+  return str.replace(/\\?([!$^*?()\[\]{}+?/])/g, '\\$1');
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e7decb4
--- /dev/null
+++ b/package.json
@@ -0,0 +1,116 @@
+{
+  "name": "braces",
+  "description": "Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces specification, without sacrificing speed.",
+  "version": "2.0.2",
+  "homepage": "https://github.com/jonschlinkert/braces",
+  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
+  "contributors": [
+    "Brian Woodward <brian.woodward at gmail.com> (https://github.com/doowb)",
+    "Elan Shanker (https://github.com/es128)",
+    "Eugene Sharygin (https://github.com/eush77)",
+    "hemanth.hm <hemanth.hm at gmail.com> (http://h3manth.com)",
+    "Jon Schlinkert <jon.schlinkert at sellside.com> (http://twitter.com/jonschlinkert)"
+  ],
+  "repository": "jonschlinkert/braces",
+  "bugs": {
+    "url": "https://github.com/jonschlinkert/braces/issues"
+  },
+  "license": "MIT",
+  "files": [
+    "index.js",
+    "lib"
+  ],
+  "main": "index.js",
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "mocha"
+  },
+  "dependencies": {
+    "arr-flatten": "^1.0.1",
+    "array-unique": "^0.3.2",
+    "debug": "^2.2.0",
+    "define-property": "^0.2.5",
+    "extend-shallow": "^2.0.1",
+    "fill-range": "^3.0.3",
+    "isobject": "^2.1.0",
+    "regex-not": "^1.0.0",
+    "repeat-element": "^1.1.2",
+    "snapdragon": "^0.8.1",
+    "to-regex": "^3.0.1"
+  },
+  "devDependencies": {
+    "ansi-cyan": "^0.1.1",
+    "benchmarked": "^0.2.5",
+    "brace-expansion": "^1.1.6",
+    "cross-spawn": "^4.0.2",
+    "fs-exists-sync": "^0.1.0",
+    "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",
+    "is-windows": "^0.2.0",
+    "minimatch": "^3.0.3",
+    "mocha": "^3.1.2",
+    "noncharacters": "^1.1.0",
+    "pretty-bytes": "^4.0.2",
+    "text-table": "^0.2.0",
+    "time-diff": "^0.3.1",
+    "yargs-parser": "^4.0.2"
+  },
+  "keywords": [
+    "alpha",
+    "alphabetical",
+    "bash",
+    "brace",
+    "braces",
+    "expand",
+    "expansion",
+    "filepath",
+    "fill",
+    "fs",
+    "glob",
+    "globbing",
+    "letter",
+    "match",
+    "matches",
+    "matching",
+    "number",
+    "numerical",
+    "path",
+    "range",
+    "ranges",
+    "sh"
+  ],
+  "verb": {
+    "toc": false,
+    "layout": "default",
+    "tasks": [
+      "readme"
+    ],
+    "lint": {
+      "reflinks": true
+    },
+    "plugins": [
+      "gulp-format-md"
+    ],
+    "related": {
+      "list": [
+        "expand-brackets",
+        "extglob",
+        "fill-range",
+        "micromatch",
+        "nanomatch"
+      ]
+    },
+    "reflinks": [
+      "brace-expansion",
+      "to-regex-range",
+      "verb",
+      "verb-generate-readme"
+    ]
+  }
+}
diff --git a/test/bash.expanded.js b/test/bash.expanded.js
new file mode 100644
index 0000000..b58b81a
--- /dev/null
+++ b/test/bash.expanded.js
@@ -0,0 +1,411 @@
+'use strict';
+
+var extend = require('extend-shallow');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+/**
+ * Bash 4.3 unit tests with `braces.expand()`
+ */
+
+describe('bash.expanded', function() {
+  var fixtures = [
+    [ 'a{b,c{1..50}/{foo,bar,baz}/,g}h/i', {}, [ 'abh/i', 'ac1/bar/h/i', 'ac1/baz/h/i', 'ac1/foo/h/i', 'ac10/bar/h/i', 'ac10/baz/h/i', 'ac10/foo/h/i', 'ac11/bar/h/i', 'ac11/baz/h/i', 'ac11/foo/h/i', 'ac12/bar/h/i', 'ac12/baz/h/i', 'ac12/foo/h/i', 'ac13/bar/h/i', 'ac13/baz/h/i', 'ac13/foo/h/i', 'ac14/bar/h/i', 'ac14/baz/h/i', 'ac14/foo/h/i', 'ac15/bar/h/i', 'ac15/baz/h/i', 'ac15/foo/h/i', 'ac16/bar/h/i', 'ac16/baz/h/i', 'ac16/foo/h/i', 'ac17/bar/h/i', 'ac17/baz/h/i', 'ac17/foo/h/i', 'ac18 [...]
+    [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', ' [...]
+    [ 'a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,d,x}{e,f}/g', 'a/{b,c,d,y}{e,f}/g' ] ],
+    [ 'a/\\{b,c,d\\}\\{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ],
+    [ 'a/\\{b,c,d\\}\\{e,f}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ],
+    [ 'a/\\{b,c,d\\}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ],
+    [ 'a/\\{b,c,d{x,y}}{e,f\\}/g', {}, [ 'a/{b,c,dx}{e,f}/g', 'a/{b,c,dy}{e,f}/g' ] ],
+    [ 'a/\\{b,c,d}{e,f\\}/g', {}, [ 'a/{b,c,d}{e,f}/g' ] ],
+    [ 'a/\\{b,c,d}{e,f}/g', {}, [ 'a/{b,c,d}e/g', 'a/{b,c,d}f/g' ] ],
+    [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ],
+    [ 'a/\\{{b,c}{e,f}/g', {}, [ 'a/{be/g', 'a/{bf/g', 'a/{ce/g', 'a/{cf/g' ] ],
+    [ 'a/\\{{b,c}{e,f}\\}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ],
+    [ 'a/\\{{b,c}{e,f}}/g', {}, [ 'a/{be}/g', 'a/{bf}/g', 'a/{ce}/g', 'a/{cf}/g' ] ],
+    [ 'a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, [ 'a/b/b/h/i', 'a/b/c/h/i', 'a/b/d/h/i', 'a/b/ef/h/i', 'a/b/eg/h/i', 'a/b/w/y/h/i', 'a/b/w/z/h/i', 'a/b/x/y/h/i', 'a/b/x/z/h/i' ] ],
+    [ 'a/{b,c,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g', 'a/de/g', 'a/df/g' ] ],
+    [ 'a/{b,c\\,d}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c,de/g', 'a/c,df/g' ] ],
+    [ 'a/{b,c\\}}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/c}e/g', 'a/c}f/g' ] ],
+    [ 'a/{b,c}', {}, [ 'a/b', 'a/c' ] ],
+    [ 'a/{b,c}d{e,f}/g', {}, [ 'a/bde/g', 'a/bdf/g', 'a/cde/g', 'a/cdf/g' ] ],
+    [ 'a/{b,c}{e,f}/g', {}, [ 'a/be/g', 'a/bf/g', 'a/ce/g', 'a/cf/g' ] ],
+    [ 'a/{b,c}{e,f}{g,h,i}/k', {}, [ 'a/beg/k', 'a/beh/k', 'a/bei/k', 'a/bfg/k', 'a/bfh/k', 'a/bfi/k', 'a/ceg/k', 'a/ceh/k', 'a/cei/k', 'a/cfg/k', 'a/cfh/k', 'a/cfi/k' ] ],
+    [ 'a/{b,{c,d},e}/f', {}, [ 'a/b/f', 'a/c/f', 'a/d/f', 'a/e/f' ] ],
+    [ 'a/{b,{c,d}/{e,f},g}/h', {}, [ 'a/b/h', 'a/c/e/h', 'a/c/f/h', 'a/d/e/h', 'a/d/f/h', 'a/g/h' ] ],
+    [ 'a/{b{c,d},e{f,g}h{i,j}}/k', {}, [ 'a/bc/k', 'a/bd/k', 'a/efhi/k', 'a/efhj/k', 'a/eghi/k', 'a/eghj/k' ] ],
+    [ 'a/{b{c,d},e}/f', {}, [ 'a/bc/f', 'a/bd/f', 'a/e/f' ] ],
+    [ 'a/{b{c,d}e{f,g}h{i,j}}/k', {}, [ 'a/{bcefhi}/k', 'a/{bcefhj}/k', 'a/{bceghi}/k', 'a/{bceghj}/k', 'a/{bdefhi}/k', 'a/{bdefhj}/k', 'a/{bdeghi}/k', 'a/{bdeghj}/k' ] ],
+    [ 'a/{b{c,d}e{f,g},h{i,j}}/k', {}, [ 'a/bcef/k', 'a/bceg/k', 'a/bdef/k', 'a/bdeg/k', 'a/hi/k', 'a/hj/k' ] ],
+    [ 'a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, [ 'a/x/1cdf.md', 'a/x/1cdf.txt', 'a/x/1cef.md', 'a/x/1cef.txt', 'a/x/2cdf.md', 'a/x/2cdf.txt', 'a/x/2cef.md', 'a/x/2cef.txt', 'a/x/3cdf.md', 'a/x/3cdf.txt', 'a/x/3cef.md', 'a/x/3cef.txt', 'a/x/4cdf.md', 'a/x/4cdf.txt', 'a/x/4cef.md', 'a/x/4cef.txt', 'a/x/5cdf.md', 'a/x/5cdf.txt', 'a/x/5cef.md', 'a/x/5cef.txt', 'a/y/1cdf.md', 'a/y/1cdf.txt', 'a/y/1cef.md', 'a/y/1cef.txt', 'a/y/2cdf.md', 'a/y/2cdf.txt', 'a/y/2cef.md', 'a/y/2cef.txt', 'a/y/3cdf.md [...]
+    [ 'a/{x,z}{b,{c,d}/{e,f},g}/h', {}, [ 'a/xb/h', 'a/xc/e/h', 'a/xc/f/h', 'a/xd/e/h', 'a/xd/f/h', 'a/xg/h', 'a/zb/h', 'a/zc/e/h', 'a/zc/f/h', 'a/zd/e/h', 'a/zd/f/h', 'a/zg/h' ] ],
+    [ 'a/{x,{1..5},y}/c{d}e', {}, [ 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/x/c{d}e', 'a/y/c{d}e' ] ],
+    [ 'a/{{a,b}/{c,d}}/z', {}, [ 'a/{a/c}/z', 'a/{a/d}/z', 'a/{b/c}/z', 'a/{b/d}/z' ] ],
+    [ 'a/{{b,c}/{d,e}}', {}, [ 'a/{b/d}', 'a/{b/e}', 'a/{c/d}', 'a/{c/e}' ] ],
+    [ 'a/{{b,c}/{d,e}}/f', {}, [ 'a/{b/d}/f', 'a/{b/e}/f', 'a/{c/d}/f', 'a/{c/e}/f' ] ],
+    [ 'a{0..3}d', {}, [ 'a0d', 'a1d', 'a2d', 'a3d' ] ],
+    [ 'a{b}c', {}, [ 'a{b}c' ] ],
+    [ 'foo {1,2} bar', {}, [ 'foo 1 bar', 'foo 2 bar' ] ],
+    [ 'x{10..1}y', {}, [ 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y' ] ],
+    [ 'x{3..3}y', {}, [ 'x3y' ] ],
+    [ '{ }', {}, [ '{ }' ] ],
+    [ '{', {}, [ '{' ] ],
+    [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ],
+    [ '{10..1}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+    [ '{3..3}', {}, [ '3' ] ],
+    [ '{5..8}', {}, [ '5', '6', '7', '8' ] ],
+    [ '{9..-4}', {}, [ '-1', '-2', '-3', '-4', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+    [ '{a,b,{c,d},e}', {}, [ 'a', 'b', 'c', 'd', 'e' ] ],
+    [ '{a,b,{c,d}e}', {}, [ 'a', 'b', 'ce', 'de' ] ],
+    [ '{a,b,{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ],
+    [ '{a,b{c,d}}', {}, [ 'a', 'bc', 'bd' ] ],
+    [ '{a,b}/{c,d}', {}, [ 'a/c', 'a/d', 'b/c', 'b/d' ] ],
+    [ '{a,b}c,d\\}', {}, [ 'ac,d}', 'bc,d}' ] ],
+    [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ],
+    [ '{a,b}{c,d}', {}, [ 'ac', 'ad', 'bc', 'bd' ] ],
+    [ '{abc}', {}, [ '{abc}' ] ],
+    [ '{b{c,d},e}', {}, [ 'bc', 'bd', 'e' ] ],
+    [ '{b{c,d},e}/f', {}, [ 'bc/f', 'bd/f', 'e/f' ] ],
+    [ 'x,y,{abc},trie', {}, [ 'x,y,{abc},trie' ] ],
+    [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ '{{a,b},{c,d}}', {}, [ 'a', 'b', 'c', 'd' ] ],
+    [ '{{a,b}/{c,d}}', {}, [ '{a/c}', '{a/d}', '{b/c}', '{b/d}' ] ],
+    [ '{{a,b}/{c,d}}/z', {}, [ '{a/c}/z', '{a/d}/z', '{b/c}/z', '{b/d}/z' ] ],
+    [ '{}', {}, [ '{}' ] ],
+
+    // should ignore globs
+    [ '}', {}, [ '}' ] ],
+
+    'should ignore globs',
+
+    [ '{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, [ 'assemble-generate-*.js', 'assemblefile.js', 'generate.js', 'generator.js', 'update-generate-*.js', 'updatefile.js', 'verb-generate-*.js', 'verbfile.js' ] ],
+    [ '**/{foo,bar}.js', {}, [ '**/bar.js', '**/foo.js' ] ],
+    [ '**/{1..5}/a.js', {}, [ '**/1/a.js', '**/2/a.js', '**/3/a.js', '**/4/a.js', '**/5/a.js' ] ],
+    [ '**/{a,b,c}/*.js', {}, [ '**/a/*.js', '**/b/*.js', '**/c/*.js' ] ],
+    [ '**/{a,b,*}/*.js', {}, [ '**/*/*.js', '**/a/*.js', '**/b/*.js' ] ],
+    [ '**/{**,b,*}/*.js', {}, [ '**/**/*.js', '**/*/*.js', '**/b/*.js' ] ],
+    [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ],
+    [ 'ff{c,b,a}', {}, [ 'ffa', 'ffb', 'ffc' ] ],
+    [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ],
+    [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'xbracesy' ] ],
+    [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ],
+    [ '{braces,{0..10}}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ '{l,n,m}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ],
+    [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ '{{1..10..2},braces}', {}, [ '1', '3', '5', '7', '9', 'braces' ] ],
+    [ '{{1..10},braces}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ],
+    [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ],
+    [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ],
+    [ '{a,b}{{a,b},a,b}', {}, [ 'aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb' ] ],
+    [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ],
+    [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ],
+    [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ],
+    [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ],
+    [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ],
+    [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ],
+    [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ],
+
+    'should gracefully handle large ranges (`braces` handles these fine,', 'they are tested elsewhere, but they break all the other reference libs)',
+
+    [ '{214748364..2147483649}', { skip: true } ],
+    [ '{2147483645..2147483649}', { skip: true } ],
+
+    'should handle invalid sets',
+
+    [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ],
+    [ '{1..10,braces}', {}, [ '1..10', 'braces' ] ],
+
+    'should not expand escaped braces',
+
+    [ '\\{a,b,c,d,e}', {}, [ '{a,b,c,d,e}' ] ],
+    [ 'a/\\{b,c}/{d,e}/f', {}, [ 'a/{b,c}/d/f', 'a/{b,c}/e/f' ] ],
+    [ 'a/\\{x,y}/cde', {}, [ 'a/{x,y}/cde' ] ],
+    [ 'a/b/c/{x,y\\}', {}, [ 'a/b/c/{x,y}' ] ],
+    [ 'a/{z,\\{a,b,c,d,e}/d', {}, [ 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d', 'a/{a/d' ] ],
+    [ 'abcd{efgh', {}, [ 'abcd{efgh' ] ],
+    [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ],
+    [ '{abc}', {}, [ '{abc}' ] ],
+    [ '{x,y,\\{a,b,c\\}}', {}, [ 'b', 'c}', 'x', 'y', '{a' ] ],
+    [ '{x,y,{a,b,c\\}}', {}, [ '{x,y,a', '{x,y,b', '{x,y,c}' ] ],
+    [ '{x,y,{abc},trie}', {}, [ 'trie', 'x', 'y', '{abc}' ] ],
+    [ './\\{x,y}/{a..z..3}/', {}, [ './{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/' ] ],
+
+    'should not expand escaped commas',
+
+    [ '{x\\,y,\\{abc\\},trie}', {}, [ 'trie', 'x,y', '{abc}' ] ],
+    [ 'a{b\\,c\\,d}e', {}, [ 'a{b,c,d}e' ] ],
+    [ 'a{b\\,c}d', {}, [ 'a{b,c}d' ] ],
+    [ '{abc\\,def}', {}, [ '{abc,def}' ] ],
+    [ '{abc\\,def,ghi}', {}, [ 'abc,def', 'ghi' ] ],
+    [ 'a/{b,c}/{x\\,y}/d/e', {}, [ 'a/b/{x,y}/d/e', 'a/c/{x,y}/d/e' ] ],
+
+    'should handle empty braces',
+
+    [ '{ }', {}, [ '{ }' ] ],
+    [ '{', {}, [ '{' ] ],
+    [ '{}', {}, [ '{}' ] ],
+    [ '}', {}, [ '}' ] ],
+
+    'should escape braces when only one value is defined',
+
+    [ 'a{b}c', {}, [ 'a{b}c' ] ],
+    [ 'a/b/c{d}e', {}, [ 'a/b/c{d}e' ] ],
+
+    'should escape closing braces when open is not defined',
+
+    [ '{a,b}c,d}', {}, [ 'ac,d}', 'bc,d}' ] ],
+
+    'should not expand braces in sets with es6/bash-like variables',
+
+    [ 'abc/${ddd}/xyz', {}, [ 'abc/${ddd}/xyz' ] ],
+    [ 'a${b}c', {}, [ 'a${b}c' ] ],
+    [ 'a/{${b},c}/d', {}, [ 'a/${b}/d', 'a/c/d' ] ],
+    [ 'a${b,d}/{foo,bar}c', {}, [ 'a${b,d}/barc', 'a${b,d}/fooc' ] ],
+
+    'should support sequence brace operators',
+
+    [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ],
+    [ 'ff{c,b,a}', {}, [ 'ffa', 'ffb', 'ffc' ] ],
+    [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ],
+    [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'xbracesy' ] ],
+    [ '{a,b,c}', {}, [ 'a', 'b', 'c' ] ],
+    [ '{braces,{0..10}}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ '{l,n,m}xyz', {}, [ 'lxyz', 'mxyz', 'nxyz' ] ],
+    [ '{{0..10},braces}', {}, [ '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+    [ '{{1..10..2},braces}', {}, [ '1', '3', '5', '7', '9', 'braces' ] ],
+    [ '{{1..10},braces}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'braces' ] ],
+
+    'should expand multiple sets',
+
+    [ 'a/{a,b}/{c,d}/e', {}, [ 'a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e' ] ],
+    [ 'a{b,c}d{e,f}g', {}, [ 'abdeg', 'abdfg', 'acdeg', 'acdfg' ] ],
+    [ 'a/{x,y}/c{d,e}f.{md,txt}', {}, [ 'a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt' ] ],
+
+    'should expand nested sets',
+
+    [ '{a,b}{{a,b},a,b}', {}, [ 'aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb' ] ],
+    [ '/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, [ '/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex' ] ],
+    [ 'a{b,c{d,e}f}g', {}, [ 'abg', 'acdfg', 'acefg' ] ],
+    [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ],
+    [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ],
+    [ 'a{b,c{d,e},h}x/z', {}, [ 'abx/z', 'acdx/z', 'acex/z', 'ahx/z' ] ],
+    [ 'a{b,c{d,e},h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz' ] ],
+    [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ],
+    [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ],
+
+    'should ignore glob characters',
+
+    [ 'a/b/{d,e}/*.js', {}, [ 'a/b/d/*.js', 'a/b/e/*.js' ] ],
+    [ 'a/**/c/{d,e}/f*.js', {}, [ 'a/**/c/d/f*.js', 'a/**/c/e/f*.js' ] ],
+    [ 'a/**/c/{d,e}/f*.{md,txt}', {}, [ 'a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt' ] ],
+    [ 'a/b/{d,e,[1-5]}/*.js', {}, [ 'a/b/[1-5]/*.js', 'a/b/d/*.js', 'a/b/e/*.js' ] ],
+
+    'should work with leading and trailing commas',
+
+    [ 'a{b,}c', {}, [ 'abc', 'ac' ] ],
+    [ 'a{,b}c', {}, [ 'abc', 'ac' ] ],
+
+    'should handle spaces',
+
+    [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', ' [...]
+    [ 'a{ ,c{d, },h}x', {}, [ 'a x', 'ac x', 'acdx', 'ahx' ] ],
+    [ 'a{ ,c{d, },h} ', {}, [ 'a  ', 'ac  ', 'acd ', 'ah ' ] ],
+
+    'see https://github.com/jonschlinkert/micromatch/issues/66',
+
+    [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ] ],
+
+    'should not try to expand ranges with decimals',
+
+    [ '{1.1..2.1}', {}, [ '{1.1..2.1}' ] ],
+    [ '{1.1..~2.1}', {}, [ '{1.1..~2.1}' ] ],
+
+    'should escape invalid ranges',
+
+    [ '{1..0f}', {}, [ '{1..0f}' ] ],
+    [ '{1..10..ff}', {}, [ '{1..10..ff}' ] ],
+    [ '{1..10.f}', {}, [ '{1..10.f}' ] ],
+    [ '{1..10f}', {}, [ '{1..10f}' ] ],
+    [ '{1..20..2f}', {}, [ '{1..20..2f}' ] ],
+    [ '{1..20..f2}', {}, [ '{1..20..f2}' ] ],
+    [ '{1..2f..2}', {}, [ '{1..2f..2}' ] ],
+    [ '{1..ff..2}', {}, [ '{1..ff..2}' ] ],
+    [ '{1..ff}', {}, [ '{1..ff}' ] ],
+    [ '{1.20..2}', {}, [ '{1.20..2}' ] ],
+
+    'should handle weirdly-formed brace expansions (fixed in post-bash-3.1)',
+
+    [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ],
+    [ 'a-{bdef-{g,i}-c', {}, [ 'a-{bdef-g-c', 'a-{bdef-i-c' ] ],
+
+    'should not expand quoted strings',
+
+    [ '{"klklkl"}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ],
+    [ '{"x,x"}', {}, [ '{x,x}' ] ],
+
+    'should escaped outer braces in nested non-sets',
+
+    [ '{a-{b,c,d}}', {}, [ '{a-b}', '{a-c}', '{a-d}' ] ],
+    [ '{a,{a-{b,c,d}}}', {}, [ 'a', '{a-b}', '{a-c}', '{a-d}' ] ],
+
+    'should escape imbalanced braces',
+
+    [ 'abc{', {}, [ 'abc{' ] ],
+    [ '{abc{', {}, [ '{abc{' ] ],
+    [ '{abc', {}, [ '{abc' ] ],
+    [ '}abc', {}, [ '}abc' ] ],
+    [ 'ab{c', {}, [ 'ab{c' ] ],
+    [ 'ab{c', {}, [ 'ab{c' ] ],
+    [ '{{a,b}', {}, [ '{a', '{b' ] ],
+    [ '{a,b}}', {}, [ 'a}', 'b}' ] ],
+    [ 'a{b{c{d,e}f}gh', {}, [ 'a{b{cdf}gh', 'a{b{cef}gh' ] ],
+    [ 'a{b{c{d,e}f}g}h', {}, [ 'a{b{cdf}g}h', 'a{b{cef}g}h' ] ],
+    [ 'f{x,y{{g,z}}h}', {}, [ 'fx', 'fy{g}h', 'fy{z}h' ] ],
+    [ 'z{a,b},c}d', {}, [ 'za,c}d', 'zb,c}d' ] ],
+    [ 'a{b{c{d,e}f{x,y{{g}h', {}, [ 'a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h' ] ],
+    [ 'f{x,y{{g}h', {}, [ 'f{x,y{{g}h' ] ],
+    [ 'f{x,y{{g}}h', {}, [ 'f{x,y{{g}}h' ] ],
+    [ 'a{b{c{d,e}f{x,y{}g}h', {}, [ 'a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh' ] ],
+    [ 'f{x,y{}g}h', {}, [ 'fxh', 'fy{}gh' ] ],
+    [ 'z{a,b{,c}d', {}, [ 'z{a,bcd', 'z{a,bd' ] ],
+
+    'should expand numeric ranges',
+
+    [ 'a{0..3}d', {}, [ 'a0d', 'a1d', 'a2d', 'a3d' ] ],
+    [ 'x{10..1}y', {}, [ 'x10y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y' ] ],
+    [ 'x{3..3}y', {}, [ 'x3y' ] ],
+    [ '{1..10}', {}, [ '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+    [ '{1..3}', {}, [ '1', '2', '3' ] ],
+    [ '{1..9}', {}, [ '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+    [ '{10..1}y', {}, [ '10y', '1y', '2y', '3y', '4y', '5y', '6y', '7y', '8y', '9y' ] ],
+    [ '{3..3}', {}, [ '3' ] ],
+    [ '{5..8}', {}, [ '5', '6', '7', '8' ] ],
+
+    'should expand ranges with negative numbers',
+
+    [ '{-10..-1}', {}, [ '-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9' ] ],
+    [ '{-20..0}', {}, [ '-1', '-10', '-11', '-12', '-13', '-14', '-15', '-16', '-17', '-18', '-19', '-2', '-20', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0' ] ],
+    [ '{0..-5}', {}, [ '-1', '-2', '-3', '-4', '-5', '0' ] ],
+    [ '{9..-4}', {}, [ '-1', '-2', '-3', '-4', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+
+    'should expand alphabetical ranges',
+
+    [ '0{1..9}/{10..20}', {}, [ '01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16', ' [...]
+    [ '0{a..d}0', {}, [ '0a0', '0b0', '0c0', '0d0' ] ],
+    [ 'a/{b..d}/e', {}, [ 'a/b/e', 'a/c/e', 'a/d/e' ] ],
+    [ '{1..f}', { minimatch: false }, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f' ] ],
+    [ '{a..A}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' ] ],
+    [ '{A..a}', {}, [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a' ] ],
+    [ '{a..e}', {}, [ 'a', 'b', 'c', 'd', 'e' ] ],
+    [ '{A..E}', {}, [ 'A', 'B', 'C', 'D', 'E' ] ],
+    [ '{a..f}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ],
+    [ '{a..z}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ] ],
+    [ '{E..A}', {}, [ 'A', 'B', 'C', 'D', 'E' ] ],
+    [ '{f..1}', { minimatch: false }, [ 'f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1' ] ],
+    [ '{f..a}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ],
+    [ '{f..f}', {}, [ 'f' ] ],
+
+    'should expand multiple ranges',
+
+    [ 'a/{b..d}/e/{f..h}', {}, [ 'a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h' ] ],
+
+    'should expand numerical ranges - positive and negative',
+
+    [ '{-10..10}', {}, [ '-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ] ],
+
+    'HEADS UP! If you\'re using the `--mm` flag minimatch freezes on these', 'should expand large numbers',
+
+    [ '{2147483645..2147483649}', { minimatch: false, optimize: true }, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ],
+    [ '{214748364..2147483649}', { minimatch: false, optimize: true }, [ '(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])' ] ],
+
+    'should expand ranges using steps',
+
+    [ '{1..10..1}', { optimize: false }, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ],
+    [ '{1..10..2}', { optimize: false }, [ '1', '3', '5', '7', '9' ] ],
+    [ '{1..20..20}', { optimize: false }, [ '1' ] ],
+    [ '{1..20..20}', { optimize: false }, [ '1' ] ],
+    [ '{1..20..20}', { optimize: false }, [ '1' ] ],
+    [ '{1..20..2}', { optimize: false }, [ '1', '3', '5', '7', '9', '11', '13', '15', '17', '19' ] ],
+    [ '{10..0..2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ],
+    [ '{10..1..2}', { optimize: false }, [ '10', '8', '6', '4', '2' ] ],
+    [ '{100..0..5}', { optimize: false }, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ],
+    [ '{2..10..1}', { optimize: false }, [ '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ],
+    [ '{2..10..2}', { optimize: false }, [ '2', '4', '6', '8', '10' ] ],
+    [ '{2..10..3}', { optimize: false }, [ '2', '5', '8' ] ],
+    [ '{a..z..2}', { optimize: false }, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ],
+
+    'should expand positive ranges with negative steps',
+
+    [ '{10..0..-2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ],
+
+    'should expand negative ranges using steps',
+
+    [ '{-1..-10..-2}', { optimize: false }, [ '-1', '-3', '-5', '-7', '-9' ] ],
+    [ '{-1..-10..2}', { optimize: false }, [ '-1', '-3', '-5', '-7', '-9' ] ],
+    [ '{-10..-2..2}', { optimize: false }, [ '-10', '-8', '-6', '-4', '-2' ] ],
+    [ '{-2..-10..1}', { optimize: false }, [ '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10' ] ],
+    [ '{-2..-10..2}', { optimize: false }, [ '-2', '-4', '-6', '-8', '-10' ] ],
+    [ '{-2..-10..3}', { optimize: false }, [ '-2', '-5', '-8' ] ],
+    [ '{-50..-0..5}', { optimize: false }, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ],
+    [ '{-9..9..3}', { optimize: false }, [ '-9', '-6', '-3', '0', '3', '6', '9' ] ],
+    [ '{10..1..-2}', { optimize: false }, [ '10', '8', '6', '4', '2' ] ],
+    [ '{100..0..-5}', { optimize: false }, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ],
+
+    'should expand alpha ranges with steps',
+
+    [ '{a..e..2}', { optimize: false }, [ 'a', 'c', 'e' ] ],
+    [ '{E..A..2}', { optimize: false }, [ 'E', 'C', 'A' ] ],
+    [ '{a..z..2}', { optimize: false }, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ],
+    [ '{z..a..-2}', { optimize: false }, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ],
+
+    'should expand alpha ranges with negative steps',
+
+    [ '{z..a..-2}', { optimize: false }, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ],
+
+    'should handle unwanted zero-padding (fixed post-bash-4.0)',
+
+    [ '{10..0..2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ],
+    [ '{10..0..-2}', { optimize: false }, [ '10', '8', '6', '4', '2', '0' ] ],
+    [ '{-50..-0..5}', { optimize: false }, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ],
+
+    'should work with dots in file paths',
+
+    [ '../{1..3}/../foo', {}, [ '../1/../foo', '../2/../foo', '../3/../foo' ] ],
+    [ '../{2..10..2}/../foo', { optimize: false }, [ '../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo' ] ],
+    [ '../{1..3}/../{a,b,c}/foo', {}, [ '../1/../a/foo', '../1/../b/foo', '../1/../c/foo', '../2/../a/foo', '../2/../b/foo', '../2/../c/foo', '../3/../a/foo', '../3/../b/foo', '../3/../c/foo' ] ],
+    [ './{a..z..3}/', { optimize: false }, [ './a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/' ] ],
+    [ './{"x,y"}/{a..z..3}/', { minimatch: false, optimize: false }, [ './{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/' ] ],
+
+    'should expand a complex combination of ranges and sets',
+
+    [ 'a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, [ 'a/x/1cdf.md', 'a/x/1cdf.txt', 'a/x/1cef.md', 'a/x/1cef.txt', 'a/x/2cdf.md', 'a/x/2cdf.txt', 'a/x/2cef.md', 'a/x/2cef.txt', 'a/x/3cdf.md', 'a/x/3cdf.txt', 'a/x/3cef.md', 'a/x/3cef.txt', 'a/x/4cdf.md', 'a/x/4cdf.txt', 'a/x/4cef.md', 'a/x/4cef.txt', 'a/x/5cdf.md', 'a/x/5cdf.txt', 'a/x/5cef.md', 'a/x/5cef.txt', 'a/y/1cdf.md', 'a/y/1cdf.txt', 'a/y/1cef.md', 'a/y/1cef.txt', 'a/y/2cdf.md', 'a/y/2cdf.txt', 'a/y/2cef.md', 'a/y/2cef.txt', 'a/y/3cdf.md [...]
+
+    'should expand complex sets and ranges in `bash` mode',
+
+    [ 'a/{x,{1..5},y}/c{d}e', {}, [ 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/x/c{d}e', 'a/y/c{d}e' ] ]
+  ];
+
+  fixtures.forEach(function(arr) {
+    if (typeof arr === 'string') {
+      return;
+    }
+
+    var options = extend({}, arr[1]);
+    var pattern = arr[0];
+    var expected = arr[2];
+
+    if (options.skip === true) {
+      return;
+    }
+
+    it('should compile: ' + pattern, function() {
+      match(pattern, expected, options);
+    });
+  });
+});
diff --git a/test/bash.optimized.js b/test/bash.optimized.js
new file mode 100644
index 0000000..848d7f2
--- /dev/null
+++ b/test/bash.optimized.js
@@ -0,0 +1,409 @@
+'use strict';
+
+var extend = require('extend-shallow');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.optimize(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+/**
+ * Bash 4.3 unit tests with `braces.optimize()`
+ */
+
+describe('bash.expanded', function() {
+  var fixtures = [
+    ['a{b,c{1..100}/{foo,bar}/,h}x/z', {}, ['a(b|c([1-9]|[1-9][0-9]|100)/(foo|bar)/|h)x/z']],
+    ['0{1..9} {10..20}', {}, ['0([1-9]) (1[0-9]|20)']],
+    ['{a,b,c,d,e}', {}, ['(a|b|c|d|e)']],
+    ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']],
+    ['a${b}c', {}, ['a${b}c']],
+    ['a/\\{b,c,d,{x,y}}{e,f\\}/g', {}, ['a/{b,c,d,(x|y)}{e,f}/g']],
+    ['a/\\{b,c,d\\}\\{e,f\\}/g', {}, ['a/{b,c,d}{e,f}/g']],
+    ['a/\\{b,c,d\\}\\{e,f}/g', {}, ['a/{b,c,d}{e,f}/g']],
+    ['a/\\{b,c,d\\}{e,f}/g', {}, ['a/{b,c,d}(e|f)/g']],
+    ['a/\\{b,c,d{x,y}}{e,f\\}/g', {}, ['a/{b,c,d(x|y)}{e,f}/g']],
+    ['a/\\{b,c,d}{e,f\\}/g', {}, ['a/{b,c,d}{e,f}/g']],
+    ['a/\\{b,c,d}{e,f}/g', {}, ['a/{b,c,d}(e|f)/g']],
+    ['a/\\{x,y}/cde', {}, ['a/{x,y}/cde']],
+    ['a/\\{{b,c}{e,f}/g', {}, ['a/{(b|c)(e|f)/g']],
+    ['a/\\{{b,c}{e,f}\\}/g', {}, ['a/{(b|c)(e|f)}/g']],
+    ['a/\\{{b,c}{e,f}}/g', {}, ['a/{(b|c)(e|f)}/g']],
+    ['a/b/c/{x,y\\}', {}, ['a/b/c/{x,y}']],
+    ['a/b/c{d}e', {}, ['a/b/c{d}e']],
+    ['a/b/{b,c,{d,e{f,g},{w,x}/{y,z}}}/h/i', {}, ['a/b/(b|c|(d|e(f|g)|(w|x)/(y|z)))/h/i']],
+    ['a/{${b},c}/d', {}, ['a/(${b}|c)/d']],
+    ['a/{b,c}}{e,f}/g', {}, ['a/(b|c)}(e|f)/g']],
+    ['a/{b,c\\,d}{e,f}/g', {}, ['a/(b|c,d)(e|f)/g']],
+    ['a/{b,c\\}}{e,f}/g', {}, ['a/(b|c})(e|f)/g']],
+    ['a/{b,c}', {}, ['a/(b|c)']],
+    ['a/{b,c}d{e,f}/g', {}, ['a/(b|c)d(e|f)/g']],
+    ['a/{b,c}{e,f}/g', {}, ['a/(b|c)(e|f)/g']],
+    ['a/{b,c}{e,f}{g,h,i}/k', {}, ['a/(b|c)(e|f)(g|h|i)/k']],
+    ['a/{b,{c,d},e}/f', {}, ['a/(b|(c|d)|e)/f']],
+    ['a/{b,{c,d}/{e,f},g}/h', {}, ['a/(b|(c|d)/(e|f)|g)/h']],
+    ['a/{b{c,d},e{f,g}h{i,j}}/k', {}, ['a/(b(c|d)|e(f|g)h(i|j))/k']],
+    ['a/{b{c,d},e}/f', {}, ['a/(b(c|d)|e)/f']],
+    ['a/{b{c,d}e{f,g}h{i,j}}/k', {}, ['a/(b(c|d)e(f|g)h(i|j))/k']],
+    ['a/{b{c,d}e{f,g},h{i,j}}/k', {}, ['a/(b(c|d)e(f|g)|h(i|j))/k']],
+    ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']],
+    ['a/{x,z}{b,{c,d}/{e,f},g}/h', {}, ['a/(x|z)(b|(c|d)/(e|f)|g)/h']],
+    ['a/{x,{1..5},y}/c{d}e', {}, ['a/(x|([1-5])|y)/c{d}e']],
+    ['a/{{a,b}/{c,d}}/z', {}, ['a/((a|b)/(c|d))/z']],
+    ['a/{{b,c}/{d,e}}', {}, ['a/((b|c)/(d|e))']],
+    ['a/{{b,c}/{d,e}}/f', {}, ['a/((b|c)/(d|e))/f']],
+    ['abc/${ddd}/xyz', {}, ['abc/${ddd}/xyz']],
+    ['abcd{efgh', {}, ['abcd{efgh']],
+    ['a{ ,c{d, },h} ', {}, ['a( |c(d| )|h) ']],
+    ['a{ ,c{d, },h}x', {}, ['a( |c(d| )|h)x']],
+    ['a{0..3}d', {}, ['a([0-3])d']],
+    ['a{b{c{d,e}f{x,y{{g}h', {}, ['a{b{c(d|e)f{x,y{{g}h']],
+    ['a{b}c', {}, ['a{b}c']],
+    ['foo {1,2} bar', {}, ['foo (1|2) bar']],
+    ['x{10..1}y', {}, ['x([1-9]|10)y']],
+    ['x{3..3}y', {}, ['x3y']],
+    ['{ }', {}, ['{ }']],
+    ['{', {}, ['{']],
+    ['{0..10,braces}', {}, ['(0..10|braces)']],
+    ['{1..0f}', {}, ['{1..0f}']],
+    ['{1..10,braces}', {}, ['(1..10|braces)']],
+    ['{1..10..ff}', {}, ['{1..10..ff}']],
+    ['{1..10.f}', {}, ['{1..10.f}']],
+    ['{1..10f}', {}, ['{1..10f}']],
+    ['{1..10}', {}, ['([1-9]|10)']],
+    ['{1..3}', {}, ['([1-3])']],
+    ['{1..9}', {}, ['([1-9])']],
+    ['{1..ff}', {}, ['{1..ff}']],
+    ['{1.20..2}', {}, ['{1.20..2}']],
+    ['{10..1}', {}, ['([1-9]|10)']],
+    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
+    ['{2147483645..2147483649}', {}, ['(214748364[5-9])']],
+    ['{3..3}', {}, ['3']],
+    ['{5..8}', {}, ['([5-8])']],
+    ['{9..-4}', {}, ['(-[1-4]|[0-9])']],
+    ['{a,b,{c,d},e}', {}, ['(a|b|(c|d)|e)']],
+    ['{a,b,{c,d}e}', {}, ['(a|b|(c|d)e)']],
+    ['{a,b,{c,d}}', {}, ['(a|b|(c|d))']],
+    ['{a,b{c,d}}', {}, ['(a|b(c|d))']],
+    ['{a,b}/{c,d}', {}, ['(a|b)/(c|d)']],
+    ['{a,b}{c,d}', {}, ['(a|b)(c|d)']],
+
+    ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']],
+    ['{{a,b},{c,d}}', {}, ['((a|b)|(c|d))']],
+    ['{{a,b}/{c,d}}', {}, ['((a|b)/(c|d))']],
+    ['{{a,b}/{c,d}}/z', {}, ['((a|b)/(c|d))/z']],
+    ['{}', {}, ['{}']],
+    ['}', {}, ['}']],
+
+    // should not process glob characters
+    ['{generate,{assemble,update,verb}{file,-generate-*},generator}.js', {}, ['(generate|(assemble|update|verb)(file|-generate-*)|generator).js']],
+    ['**/{foo,bar}.js', {}, ['**/(foo|bar).js']],
+    ['**/{1..5}/a.js', {}, ['**/([1-5])/a.js']],
+    ['**/{a,b,c}/*.js', {}, ['**/(a|b|c)/*.js']],
+    ['**/{a,b,*}/*.js', {}, ['**/(a|b|*)/*.js']],
+    ['**/{**,b,*}/*.js', {}, ['**/(**|b|*)/*.js']],
+
+    ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']],
+    ['ff{c,b,a}', {}, ['ff(c|b|a)']],
+    ['f{d,e,f}g', {}, ['f(d|e|f)g']],
+    ['x{{0..10},braces}y', {}, ['x(([0-9]|10)|braces)y']],
+    ['{1..10}', {}, ['([1-9]|10)']],
+    ['{a,b,c}', {}, ['(a|b|c)']],
+    ['{braces,{0..10}}', {}, ['(braces|([0-9]|10))']],
+    ['{l,n,m}xyz', {}, ['(l|n|m)xyz']],
+    ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']],
+    ['{{1..10..2},braces}', {bash: false }, ['((1|3|5|7|9)|braces)']],
+    ['{{1..10},braces}', {}, ['(([1-9]|10)|braces)']],
+
+    ['a/{a,b}/{c,d}/e', {}, ['a/(a|b)/(c|d)/e']],
+    ['a{b,c}d{e,f}g', {}, ['a(b|c)d(e|f)g']],
+    ['a/{x,y}/c{d,e}f.{md,txt}', {}, ['a/(x|y)/c(d|e)f.(md|txt)']],
+    ['{a,b}{{a,b},a,b}', {}, ['(a|b)((a|b)|a|b)']],
+    ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']],
+    ['a{b,c{d,e}f}g', {}, ['a(b|c(d|e)f)g']],
+    ['a{{x,y},z}b', {}, ['a((x|y)|z)b']],
+    ['f{x,y{g,z}}h', {}, ['f(x|y(g|z))h']],
+    ['a{b,c{d,e},h}x/z', {}, ['a(b|c(d|e)|h)x/z']],
+    ['a{b,c{d,e},h}x{y,z}', {}, ['a(b|c(d|e)|h)x(y|z)']],
+    ['a{b,c{d,e},{f,g}h}x{y,z}', {}, ['a(b|c(d|e)|(f|g)h)x(y|z)']],
+
+    // should handle invalid sets
+    ['{0..10,braces}', {}, ['(0..10|braces)']],
+    ['{1..10,braces}', {}, ['(1..10|braces)']],
+
+    // should not expand escaped braces
+    ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']],
+    ['a/b/c/{x,y\\}', {}, ['a/b/c/{x,y}']],
+    ['a/\\{x,y}/cde', {}, ['a/{x,y}/cde']],
+    ['abcd{efgh', {}, ['abcd{efgh']],
+    ['\\{abc\\}', {}, ['{abc}']],
+    ['{x,y,\\{a,b,c\\}}', {}, ['(x|y|{a|b|c})']],
+    ['{x,y,{a,b,c\\}}', {}, ['{x,y,(a|b|c})']],
+    ['{x\\,y,\\{abc\\},trie}', {}, ['(x,y|{abc}|trie)']],
+    ['{x,y,{abc},trie}', {}, ['(x|y|{abc}|trie)']],
+    ['x,y,{abc},trie', {}, ['x,y,{abc},trie']],
+    ['{b{c,d},e}', {}, ['(b(c|d)|e)']],
+    ['{b{c,d},e}/f', {}, ['(b(c|d)|e)/f']],
+    ['{abc}', {}, ['{abc}']],
+
+    // should handle empty braces
+    ['{ }', {}, ['{ }']],
+    ['{', {}, ['{']],
+    ['{}', {}, ['{}']],
+    ['}', {}, ['}']],
+
+    // should escape braces when only one value is defined
+    ['a{b}c', {}, ['a{b}c']],
+    ['a/b/c{d}e', {}, ['a/b/c{d}e']],
+
+    // should escape closing braces when open is not defined
+    ['{a,b}c,d}', {}, ['(a|b)c,d}']],
+    ['a,b,c,d}', {}, ['a,b,c,d}']],
+
+    // should not expand braces in sets with es6/bash-like variables
+    ['abc/${ddd}/xyz', {}, ['abc/${ddd}/xyz']],
+    ['a${b}c', {}, ['a${b}c']],
+    ['a/{${b},c}/d', {}, ['a/(${b}|c)/d']],
+    ['a${b,d}/{foo,bar}c', {}, ['a${b,d}/(foo|bar)c']],
+
+    // should not expand escaped commas
+    ['a{b\\,c\\,d}e', {}, ['a{b,c,d}e']],
+    ['a{b\\,c}d', {}, ['a{b,c}d']],
+    ['{abc\\,def}', {}, ['{abc,def}']],
+    ['{abc\\,def,ghi}', {}, ['(abc,def|ghi)']],
+    ['a/{b,c}/{x\\,y}/d/e', {}, ['a/(b|c)/{x,y}/d/e']],
+
+    // should not expand escaped braces
+    ['{a,b\\}c,d}', {}, ['(a|b}c|d)']],
+    ['\\{a,b,c,d,e}', {}, ['{a,b,c,d,e}']],
+    ['a/{z,\\{a,b,c,d,e}/d', {}, ['a/(z|{a|b|c|d|e)/d']],
+    ['a/\\{b,c}/{d,e}/f', {}, ['a/{b,c}/(d|e)/f']],
+    ['./\\{x,y}/{a..z..3}/', {}, ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']],
+
+    // should not expand escaped braces or commas
+    ['{x\\,y,\\{abc\\},trie}', {}, ['(x,y|{abc}|trie)']],
+
+    // should support sequence brace operators
+    ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']],
+    ['ff{c,b,a}', {}, ['ff(c|b|a)']],
+    ['f{d,e,f}g', {}, ['f(d|e|f)g']],
+    ['x{{0..10},braces}y', {}, ['x(([0-9]|10)|braces)y']],
+    ['{1..10}', {}, ['([1-9]|10)']],
+    ['{a,b,c}', {}, ['(a|b|c)']],
+    ['{braces,{0..10}}', {}, ['(braces|([0-9]|10))']],
+    ['{l,n,m}xyz', {}, ['(l|n|m)xyz']],
+    ['{{0..10},braces}', {}, ['(([0-9]|10)|braces)']],
+    ['{{1..10..2},braces}', {}, ['((1|3|5|7|9)|braces)']],
+    ['{{1..10},braces}', {}, ['(([1-9]|10)|braces)']],
+
+    // should expand multiple sets
+    ['a/{a,b}/{c,d}/e', {}, ['a/(a|b)/(c|d)/e']],
+    ['a{b,c}d{e,f}g', {}, ['a(b|c)d(e|f)g']],
+    ['a/{x,y}/c{d,e}f.{md,txt}', {}, ['a/(x|y)/c(d|e)f.(md|txt)']],
+
+    // should expand nested sets
+    ['{a,b}{{a,b},a,b}', {}, ['(a|b)((a|b)|a|b)']],
+    ['/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', {}, ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']],
+    ['a{b,c{d,e}f}g', {}, ['a(b|c(d|e)f)g']],
+    ['a{{x,y},z}b', {}, ['a((x|y)|z)b']],
+    ['f{x,y{g,z}}h', {}, ['f(x|y(g|z))h']],
+    ['a{b,c{d,e},h}x/z', {}, ['a(b|c(d|e)|h)x/z']],
+    ['a{b,c{d,e},h}x{y,z}', {}, ['a(b|c(d|e)|h)x(y|z)']],
+    ['a{b,c{d,e},{f,g}h}x{y,z}', {}, ['a(b|c(d|e)|(f|g)h)x(y|z)']],
+    ['a-{b{d,e}}-c', {}, ['a-{b(d|e)}-c']],
+
+    // should expand not modify non-brace characters
+    ['a/b/{d,e}/*.js', {}, ['a/b/(d|e)/*.js']],
+    ['a/**/c/{d,e}/f*.js', {}, ['a/**/c/(d|e)/f*.js']],
+    ['a/**/c/{d,e}/f*.{md,txt}', {}, ['a/**/c/(d|e)/f*.(md|txt)']],
+
+    // should work with leading and trailing commas
+    ['a{b,}c', {}, ['a(b|)c']],
+    ['a{,b}c', {}, ['a(|b)c']],
+
+    // should handle spaces
+    // Bash 4.3 says the this first one should be equivalent to `foo|(1|2)|bar
+    // That makes sense in Bash, since ' ' is a separator, but not here.
+    ['foo {1,2} bar', {}, ['foo (1|2) bar']],
+    ['0{1..9} {10..20}', {}, ['0([1-9]) (1[0-9]|20)']],
+    ['a{ ,c{d, },h}x', {}, ['a( |c(d| )|h)x']],
+    ['a{ ,c{d, },h} ', {}, ['a( |c(d| )|h) ']],
+
+    // see https://github.com/jonschlinkert/micromatch/issues/66
+    ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']],
+
+    /**
+    * Ranges
+    */
+
+    // should not try to expand ranges with decimals
+    ['{1.1..2.1}', {}, ['{1.1..2.1}']],
+    ['{1.1..~2.1}', {}, ['{1.1..~2.1}']],
+
+    // should escape invalid ranges
+    ['{1..0f}', {}, ['{1..0f}']],
+    ['{1..10..ff}', {}, ['{1..10..ff}']],
+    ['{1..10.f}', {}, ['{1..10.f}']],
+    ['{1..10f}', {}, ['{1..10f}']],
+    ['{1..20..2f}', {}, ['{1..20..2f}']],
+    ['{1..20..f2}', {}, ['{1..20..f2}']],
+    ['{1..2f..2}', {}, ['{1..2f..2}']],
+    ['{1..ff..2}', {}, ['{1..ff..2}']],
+    ['{1..ff}', {}, ['{1..ff}']],
+    ['{1.20..2}', {}, ['{1.20..2}']],
+
+    // should handle weirdly-formed brace expansions (fixed in post-bash-3.1)
+    ['a-{b{d,e}}-c', {}, ['a-{b(d|e)}-c']],
+    ['a-{bdef-{g,i}-c', {}, ['a-{bdef-(g|i)-c']],
+
+    // should not expand quoted strings
+    ['{"klklkl"}{1,2,3}', {}, ['{klklkl}(1|2|3)']],
+    ['{"x,x"}', {}, ['{x,x}']],
+
+    // should escaped outer braces in nested non-sets
+    ['{a-{b,c,d}}', {}, ['{a-(b|c|d)}']],
+    ['{a,{a-{b,c,d}}}', {}, ['(a|{a-(b|c|d)})']],
+
+    // should escape imbalanced braces
+    ['a-{bdef-{g,i}-c', {}, ['a-{bdef-(g|i)-c']],
+    ['abc{', {}, ['abc{']],
+    ['{abc{', {}, ['{abc{']],
+    ['{abc', {}, ['{abc']],
+    ['}abc', {}, ['}abc']],
+    ['ab{c', {}, ['ab{c']],
+    ['ab{c', {}, ['ab{c']],
+    ['{{a,b}', {}, ['{(a|b)']],
+    ['{a,b}}', {}, ['(a|b)}']],
+    ['abcd{efgh', {}, ['abcd{efgh']],
+    ['a{b{c{d,e}f}gh', {}, ['a{b(c(d|e)f)gh']],
+    ['a{b{c{d,e}f}g}h', {}, ['a(b(c(d|e)f)g)h']],
+    ['f{x,y{{g,z}}h}', {}, ['f(x|y((g|z))h)']],
+    ['z{a,b},c}d', {}, ['z(a|b),c}d']],
+    ['a{b{c{d,e}f{x,y{{g}h', {}, ['a{b{c(d|e)f{x,y{{g}h']],
+    ['f{x,y{{g}h', {}, ['f{x,y{{g}h']],
+    ['f{x,y{{g}}h', {}, ['f{x,y{{g}}h']],
+    ['a{b{c{d,e}f{x,y{}g}h', {}, ['a{b{c(d|e)f(x|y{}g)h']],
+    ['f{x,y{}g}h', {}, ['f(x|y{}g)h']],
+    ['z{a,b{,c}d', {}, ['z{a,b(|c)d']],
+
+    // should expand numeric ranges
+    ['a{0..3}d', {}, ['a([0-3])d']],
+    ['x{10..1}y', {}, ['x([1-9]|10)y']],
+    ['x{3..3}y', {}, ['x3y']],
+    ['{1..10}', {}, ['([1-9]|10)']],
+    ['{1..3}', {}, ['([1-3])']],
+    ['{1..9}', {}, ['([1-9])']],
+    ['{10..1}', {}, ['([1-9]|10)']],
+    ['{10..1}y', {}, ['([1-9]|10)y']],
+    ['{3..3}', {}, ['3']],
+    ['{5..8}', {}, ['([5-8])']],
+
+    // should expand ranges with negative numbers
+    ['{-10..-1}', {}, ['(-[1-9]|-10)']],
+    ['{-20..0}', {}, ['(-[1-9]|-1[0-9]|-20|0)']],
+    ['{0..-5}', {}, ['(-[1-5]|0)']],
+    ['{9..-4}', {}, ['(-[1-4]|[0-9])']],
+
+    // should expand alphabetical ranges
+    ['0{1..9}/{10..20}', {}, ['0([1-9])/(1[0-9]|20)']],
+    ['0{a..d}0', {}, ['0([a-d])0']],
+    ['a/{b..d}/e', {}, ['a/([b-d])/e']],
+    ['{1..f}', {}, ['([1-f])']],
+    ['{a..A}', {}, ['([A-a])']],
+    ['{A..a}', {}, ['([A-a])']],
+    ['{a..e}', {}, ['([a-e])']],
+    ['{A..E}', {}, ['([A-E])']],
+    ['{a..f}', {}, ['([a-f])']],
+    ['{a..z}', {}, ['([a-z])']],
+    ['{E..A}', {}, ['([A-E])']],
+    ['{f..1}', {}, ['([1-f])']],
+    ['{f..a}', {}, ['([a-f])']],
+    ['{f..f}', {}, ['f']],
+
+    // should expand multiple ranges
+    ['a/{b..d}/e/{f..h}', {}, ['a/([b-d])/e/([f-h])']],
+
+    // should expand numerical ranges - positive and negative
+    ['{-10..10}', {}, ['(-[1-9]|-?10|[0-9])']],
+
+    // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
+    // should expand large numbers
+    ['{2147483645..2147483649}', {}, ['(214748364[5-9])']],
+    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
+
+    // should expand ranges using steps
+    ['{1..10..1}', {bash: false}, ['([1-9]|10)']],
+    ['{1..10..2}', {bash: false}, ['(1|3|5|7|9)']],
+    ['{1..20..20}', {bash: false}, ['1']],
+    ['{1..20..2}', {bash: false}, ['(1|3|5|7|9|11|13|15|17|19)']],
+    ['{10..0..2}', {bash: false}, ['(10|8|6|4|2|0)']],
+    ['{10..1..2}', {bash: false}, ['(10|8|6|4|2)']],
+    ['{100..0..5}', {bash: false}, ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']],
+    ['{2..10..1}', {bash: false}, ['([2-9]|10)']],
+    ['{2..10..2}', {bash: false}, ['(2|4|6|8|10)']],
+    ['{2..10..3}', {bash: false}, ['(2|5|8)']],
+    ['{a..z..2}', {bash: false}, ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']],
+
+    // should expand positive ranges with negative steps
+    ['{10..0..-2}', {bash: false}, ['(10|8|6|4|2|0)']],
+
+    // should expand negative ranges using steps
+    ['{-1..-10..-2}', {bash: false}, ['(-(1|3|5|7|9))']],
+    ['{-1..-10..2}', {bash: false}, ['(-(1|3|5|7|9))']],
+    ['{-10..-2..2}', {bash: false}, ['(-(10|8|6|4|2))']],
+    ['{-2..-10..1}', {bash: false}, ['(-[2-9]|-10)']],
+    ['{-2..-10..2}', {bash: false}, ['(-(2|4|6|8|10))']],
+    ['{-2..-10..3}', {bash: false}, ['(-(2|5|8))']],
+    ['{-50..-0..5}', {bash: false}, ['(0|-(50|45|40|35|30|25|20|15|10|5))']],
+    ['{-9..9..3}', {bash: false}, ['(0|3|6|9|-(9|6|3))']],
+    ['{10..1..-2}', {bash: false}, ['(10|8|6|4|2)']],
+    ['{100..0..-5}', {bash: false}, ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']],
+
+    // should expand alpha ranges with steps
+    ['{a..e..2}', {bash: false}, ['(a|c|e)']],
+    ['{E..A..2}', {bash: false}, ['(E|C|A)']],
+    ['{a..z..2}', {bash: false}, ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']],
+    ['{z..a..-2}', {bash: false}, ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']],
+
+    // should expand alpha ranges with negative steps
+    ['{z..a..-2}', {bash: false}, ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']],
+
+    // unwanted zero-padding (fixed post-bash-4.0)
+    ['{10..0..2}', {bash: false}, ['(10|8|6|4|2|0)']],
+    ['{10..0..-2}', {bash: false}, ['(10|8|6|4|2|0)']],
+    ['{-50..-0..5}', {bash: false}, ['(0|-(50|45|40|35|30|25|20|15|10|5))']],
+
+    // should work with dots in file paths
+    ['../{1..3}/../foo', {}, ['../([1-3])/../foo']],
+    ['../{2..10..2}/../foo', {}, ['../(2|4|6|8|10)/../foo']],
+    ['../{1..3}/../{a,b,c}/foo', {}, ['../([1-3])/../(a|b|c)/foo']],
+    ['./{a..z..3}/', {}, ['./(a|d|g|j|m|p|s|v|y)/']],
+    ['./{"x,y"}/{a..z..3}/', {}, ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']],
+
+    // should expand a complex combination of ranges and sets
+    ['a/{x,y}/{1..5}c{d,e}f.{md,txt}', {}, ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']],
+
+    // should expand complex sets and ranges in `bash` mode
+    ['a/{x,{1..5},y}/c{d}e', {}, ['a/(x|([1-5])|y)/c{d}e']]
+  ];
+
+  fixtures.forEach(function(arr) {
+    if (typeof arr === 'string') {
+      return;
+    }
+
+    var options = extend({}, arr[1]);
+    var pattern = arr[0];
+    var expected = arr[2];
+
+    if (options.skip === true) {
+      return;
+    }
+
+    it('should compile: ' + pattern, function() {
+      match(pattern, expected, options);
+    });
+  });
+});
diff --git a/test/bash.spec.js b/test/bash.spec.js
new file mode 100644
index 0000000..862ade0
--- /dev/null
+++ b/test/bash.spec.js
@@ -0,0 +1,188 @@
+'use strict';
+
+var extend = require('extend-shallow');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+/**
+ * Bash 4.3 unit tests
+ */
+
+describe('bash', function() {
+  var fixtures = [
+    [ '{1\\.2}', {}, [ '{1.2}' ] ],
+    [ '{"x,x"}', {}, [ '{x,x}' ] ],
+    [ '{x","x}', {}, [ '{x,x}' ] ],
+    [ '\'{x,x}\'', {}, [ '{x,x}' ] ],
+    [ '{x`,`x}', {}, [ 'x`', '`x' ] ],
+    [ '\'{a,b}{{a,b},a,b}\'', {}, [ '{a,b}{{a,b},a,b}' ] ],
+    [ 'A{b,{d,e},{f,g}}Z', {}, [ 'AbZ', 'AdZ', 'AeZ', 'AfZ', 'AgZ' ] ],
+    [ 'PRE-{a,b}{{a,b},a,b}-POST', {}, [ 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-ba-POST', 'PRE-bb-POST', 'PRE-ba-POST', 'PRE-bb-POST' ] ],
+    [ '\\{a,b}{{a,b},a,b}', {}, [ '{a,b}a', '{a,b}b', '{a,b}a', '{a,b}b' ] ],
+    [ '{{a,b}', {}, [ '{a', '{b' ] ],
+    [ '{a,b}}', {}, [ 'a}', 'b}' ] ],
+    [ '{,}', {}, [] ],
+    [ 'a{,}', {}, [ 'a', 'a' ] ],
+    [ '{,}b', {}, [ 'b', 'b' ] ],
+    [ 'a{,}b', {}, [ 'ab', 'ab' ] ],
+    [ 'a{b}c', {}, [ 'a{b}c' ] ],
+    [ 'a{1..5}b', {}, [ 'a1b', 'a2b', 'a3b', 'a4b', 'a5b' ] ],
+    [ 'a{01..5}b', {}, [ 'a01b', 'a02b', 'a03b', 'a04b', 'a05b' ] ],
+    [ 'a{-01..5}b', {}, [ 'a-01b', 'a000b', 'a001b', 'a002b', 'a003b', 'a004b', 'a005b' ] ],
+    [ 'a{-01..5..3}b', {}, [ 'a-01b', 'a002b', 'a005b' ] ],
+    [ 'a{001..9}b', {}, [ 'a001b', 'a002b', 'a003b', 'a004b', 'a005b', 'a006b', 'a007b', 'a008b', 'a009b' ] ],
+    [ 'a{b,c{d,e},{f,g}h}x{y,z', {}, [ 'abx{y,z', 'acdx{y,z', 'acex{y,z', 'afhx{y,z', 'aghx{y,z' ] ],
+    [ 'a{b,c{d,e},{f,g}h}x{y,z\\}', {}, [ 'abx{y,z}', 'acdx{y,z}', 'acex{y,z}', 'afhx{y,z}', 'aghx{y,z}' ] ],
+    [ 'a{b,c{d,e},{f,g}h}x{y,z}', {}, [ 'abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz' ] ],
+    [ 'a{b{c{d,e}f{x,y{{g}h', {}, [ 'a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h' ] ],
+    [ 'a{b{c{d,e}f{x,y{}g}h', {}, [ 'a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh' ] ],
+    [ 'a{b{c{d,e}f{x,y}}g}h', {}, [ 'a{b{cdfx}g}h', 'a{b{cdfy}g}h', 'a{b{cefx}g}h', 'a{b{cefy}g}h' ] ],
+    [ 'a{b{c{d,e}f}g}h', {}, [ 'a{b{cdf}g}h', 'a{b{cef}g}h' ] ],
+    [ 'a{{x,y},z}b', {}, [ 'axb', 'ayb', 'azb' ] ],
+    [ 'f{x,y{g,z}}h', {}, [ 'fxh', 'fygh', 'fyzh' ] ],
+    [ 'f{x,y{{g,z}}h', {}, [ 'f{x,y{g}h', 'f{x,y{z}h' ] ],
+    [ 'f{x,y{{g,z}}h}', {}, [ 'fx', 'fy{g}h', 'fy{z}h' ] ],
+    [ 'f{x,y{{g}h', {}, [ 'f{x,y{{g}h' ] ],
+    [ 'f{x,y{{g}}h', {}, [ 'f{x,y{{g}}h' ] ],
+    [ 'f{x,y{}g}h', {}, [ 'fxh', 'fy{}gh' ] ],
+    [ 'z{a,b{,c}d', {}, [ 'z{a,bd', 'z{a,bcd' ] ],
+    [ 'z{a,b},c}d', {}, [ 'za,c}d', 'zb,c}d' ] ],
+    [ '{-01..5}', {}, [ '-01', '000', '001', '002', '003', '004', '005' ] ],
+    [ '{-05..100..5}', {}, [ '-05', '000', '005', '010', '015', '020', '025', '030', '035', '040', '045', '050', '055', '060', '065', '070', '075', '080', '085', '090', '095', '100' ] ],
+    [ '{-05..100}', {}, [ '-05', '-04', '-03', '-02', '-01', '000', '001', '002', '003', '004', '005', '006', '007', '008', '009', '010', '011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061 [...]
+    [ '{0..5..2}', {}, [ '0', '2', '4' ] ],
+    [ '{0001..05..2}', {}, [ '0001', '0003', '0005' ] ],
+    [ '{0001..-5..2}', {}, [ '0001', '-001', '-003', '-005' ] ],
+    [ '{0001..-5..-2}', {}, [ '0001', '-001', '-003', '-005' ] ],
+    [ '{0001..5..-2}', {}, [ '0001', '0003', '0005' ] ],
+    [ '{01..5}', {}, [ '01', '02', '03', '04', '05' ] ],
+    [ '{1..05}', {}, [ '01', '02', '03', '04', '05' ] ],
+    [ '{1..05..3}', {}, [ '01', '04' ] ],
+    [ '{05..100}', {}, [ '005', '006', '007', '008', '009', '010', '011', '012', '013', '014', '015', '016', '017', '018', '019', '020', '021', '022', '023', '024', '025', '026', '027', '028', '029', '030', '031', '032', '033', '034', '035', '036', '037', '038', '039', '040', '041', '042', '043', '044', '045', '046', '047', '048', '049', '050', '051', '052', '053', '054', '055', '056', '057', '058', '059', '060', '061', '062', '063', '064', '065', '066', '067', '068', '069', '070', '071' [...]
+    [ '{0a..0z}', {}, [ '{0a..0z}' ] ],
+    [ '{a,b\\}c,d}', {}, [ 'a', 'b}c', 'd' ] ],
+    [ '{a,b{c,d}', {}, [ '{a,bc', '{a,bd' ] ],
+    [ '{a,b}c,d}', {}, [ 'ac,d}', 'bc,d}' ] ],
+    [ '{a..F}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F' ] ],
+    [ '{A..f}', {}, [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f' ] ],
+    [ '{a..Z}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z' ] ],
+    [ '{A..z}', {}, [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ] ],
+    [ '{z..A}', {}, [ 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' ] ],
+    [ '{Z..a}', {}, [ 'Z', '[', '\\', ']', '^', '_', '`', 'a' ] ],
+    [ '{a..F..2}', {}, [ 'a', '_', ']', '[', 'Y', 'W', 'U', 'S', 'Q', 'O', 'M', 'K', 'I', 'G' ] ],
+    [ '{A..f..02}', {}, [ 'A', 'C', 'E', 'G', 'I', 'K', 'M', 'O', 'Q', 'S', 'U', 'W', 'Y', '[', ']', '_', 'a', 'c', 'e' ] ],
+    [ '{a..Z..5}', {}, [ 'a', '\\' ] ],
+    [ 'd{a..Z..5}b', {}, [ 'dab', 'd\\b' ] ],
+    [ '{A..z..10}', {}, [ 'A', 'K', 'U', '_', 'i', 's' ] ],
+    [ '{z..A..-2}', {}, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b', '`', '^', '\\', 'Z', 'X', 'V', 'T', 'R', 'P', 'N', 'L', 'J', 'H', 'F', 'D', 'B' ] ],
+    [ '{Z..a..20}', {}, [ 'Z' ] ],
+    [ '{a{,b}', {}, [ '{a', '{ab' ] ],
+    [ '{a\\},b}', {}, [ 'a}', 'b' ] ],
+    [ '{x,y{,}g}', {}, [ 'x', 'yg', 'yg' ] ],
+    [ '{x,y{}g}', {}, [ 'x', 'y{}g' ] ],
+    [ '{{a,b}', {}, [ '{a', '{b' ] ],
+    [ '{{a,b},c}', {}, [ 'a', 'b', 'c' ] ],
+    [ '{{a,b}c}', {}, [ '{ac}', '{bc}' ] ],
+    [ '{{a,b},}', {}, [ '', 'a', 'b' ] ],
+    [ 'X{{a,b},}X', {}, [ 'XaX', 'XbX', 'XX' ] ],
+    [ '{{a,b},}c', {}, [ 'ac', 'bc', 'c' ] ],
+    [ '{{a,b}.}', {}, [ '{a.}', '{b.}' ] ],
+    [ '{{a,b}}', {}, [ '{a}', '{b}' ] ],
+    [ 'X{a..#}X', {}, [ 'X{a..#}X' ] ],
+    [ '', {}, [ '' ] ],
+    [ '{-10..00}', {}, [ '-10', '-09', '-08', '-07', '-06', '-05', '-04', '-03', '-02', '-01', '000' ] ],
+    [ '{a,\\\\{a,b}c}', {}, [ 'a', '\\\\ac', '\\\\bc' ] ],
+    [ '{a,\\{a,b}c}', {}, [ 'ac}', '{ac}', 'bc}' ] ],
+    [ 'a,\\{b,c}', {}, [ 'a,{b,c}' ] ],
+    [ '{-10.\\.00}', {}, [ '{-10..00}' ] ],
+    [ 'ff{c,b,a}', {}, [ 'ffc', 'ffb', 'ffa' ] ],
+    [ 'f{d,e,f}g', {}, [ 'fdg', 'feg', 'ffg' ] ],
+    [ '{l,n,m}xyz', {}, [ 'lxyz', 'nxyz', 'mxyz' ] ],
+    [ '{abc\\,def}', {}, [ '{abc,def}' ] ],
+    [ '{abc}', {}, [ '{abc}' ] ],
+    [ '{x\\,y,\\{abc\\},trie}', {}, [ 'x,y', '{abc}', 'trie' ] ],
+    [ '{}', {}, [ '{}' ] ],
+    [ '{ }', {}, [ '{ }' ] ],
+    [ '}', {}, [ '}' ] ],
+    [ '{', {}, [ '{' ] ],
+    [ 'abcd{efgh', {}, [ 'abcd{efgh' ] ],
+    [ 'foo {1,2} bar', {}, [ 'foo 1 bar', 'foo 2 bar' ] ],
+    [ '"${var}"{x,y}', {}, [ '${var}x', '${var}y' ] ],
+    [ '{1..10}', {}, [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10' ] ],
+    [ '{0..10,braces}', {}, [ '0..10', 'braces' ] ],
+    [ '{{0..10},braces}', {}, [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces' ] ],
+    [ 'x{{0..10},braces}y', {}, [ 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y', 'xbracesy' ] ],
+    [ '{3..3}', {}, [ '3' ] ],
+    [ 'x{3..3}y', {}, [ 'x3y' ] ],
+    [ '{10..1}', {}, [ '10', '9', '8', '7', '6', '5', '4', '3', '2', '1' ] ],
+    [ '{10..1}y', {}, [ '10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y' ] ],
+    [ 'x{10..1}y', {}, [ 'x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y' ] ],
+    [ '{a..f}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f' ] ],
+    [ '{f..a}', {}, [ 'f', 'e', 'd', 'c', 'b', 'a' ] ],
+    [ '{a..A}', {}, [ 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' ] ],
+    [ '{A..a}', {}, [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a' ] ],
+    [ '{f..f}', {}, [ 'f' ] ],
+    [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', ' [...]
+    [ '{-1..-10}', {}, [ '-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10' ] ],
+    [ '{-20..0}', {}, [ '-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0' ] ],
+    [ 'a-{b{d,e}}-c', {}, [ 'a-{bd}-c', 'a-{be}-c' ] ],
+    [ 'a-{bdef-{g,i}-c', {}, [ 'a-{bdef-g-c', 'a-{bdef-i-c' ] ],
+    [ '{"klklkl"}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ],
+    [ '{"x,x"}', {}, [ '{x,x}' ] ],
+    [ '{klklkl}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ],
+    [ '{1..10..2}', {}, [ '1', '3', '5', '7', '9' ] ],
+    [ '{-1..-10..2}', {}, [ '-1', '-3', '-5', '-7', '-9' ] ],
+    [ '{-1..-10..-2}', {}, [ '-1', '-3', '-5', '-7', '-9' ] ],
+    [ '{10..1..-2}', {}, [ '10', '8', '6', '4', '2' ] ],
+    [ '{10..1..2}', {}, [ '10', '8', '6', '4', '2' ] ],
+    [ '{1..20..2}', {}, [ '1', '3', '5', '7', '9', '11', '13', '15', '17', '19' ] ],
+    [ '{1..20..20}', {}, [ '1' ] ],
+    [ '{100..0..5}', {}, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ],
+    [ '{100..0..-5}', {}, [ '100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0' ] ],
+    [ '{a..z}', {}, [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ] ],
+    [ '{a..z..2}', {}, [ 'a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y' ] ],
+    [ '{z..a..-2}', {}, [ 'z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b' ] ],
+    [ '{2147483645..2147483649}', {}, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ],
+    [ '{10..0..2}', {}, [ '10', '8', '6', '4', '2', '0' ] ],
+    [ '{10..0..-2}', {}, [ '10', '8', '6', '4', '2', '0' ] ],
+    [ '{-50..-0..5}', {}, [ '-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0' ] ],
+    [ '{1..10.f}', {}, [ '{1..10.f}' ] ],
+    [ '{1..ff}', {}, [ '{1..ff}' ] ],
+    [ '{1..10..ff}', {}, [ '{1..10..ff}' ] ],
+    [ '{1.20..2}', {}, [ '{1.20..2}' ] ],
+    [ '{1..20..f2}', {}, [ '{1..20..f2}' ] ],
+    [ '{1..20..2f}', {}, [ '{1..20..2f}' ] ],
+    [ '{1..2f..2}', {}, [ '{1..2f..2}' ] ],
+    [ '{1..ff..2}', {}, [ '{1..ff..2}' ] ],
+    [ '{1..ff}', {}, [ '{1..ff}' ] ],
+    [ '{1..0f}', {}, [ '{1..0f}' ] ],
+    [ '{1..10f}', {}, [ '{1..10f}' ] ],
+    [ '{1..10.f}', {}, [ '{1..10.f}' ] ],
+    [ '{},b}.h', {}, [ '{},b}.h' ] ],
+    [ 'y{\\},a}x', {}, [ 'y}x', 'yax' ] ],
+    [ '{}a,b}c', {}, [ '{}a,b}c' ] ]
+  ];
+
+  fixtures.forEach(function(arr) {
+    if (typeof arr === 'string') {
+      return;
+    }
+
+    var options = extend({}, arr[1]);
+    var pattern = arr[0];
+    var expected = arr[2];
+
+    if (options.skip === true) {
+      return;
+    }
+
+    it('should compile: ' + pattern, function() {
+      match(pattern, expected, options);
+    });
+  });
+});
diff --git a/test/brace-expansion.js b/test/brace-expansion.js
new file mode 100644
index 0000000..a3d8f63
--- /dev/null
+++ b/test/brace-expansion.js
@@ -0,0 +1,91 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+/**
+ * All of the unit tests from brace-expansion v1.1.6
+ * https://github.com/juliangruber/brace-expansion
+ */
+
+describe('unit tests from brace-expand', function() {
+  describe('sequences', function() {
+    it('numeric sequences', function() {
+      match('a{1..2}b{2..3}c', ['a1b2c', 'a1b3c', 'a2b2c', 'a2b3c']);
+      match('{1..2}{2..3}', ['12', '13', '22', '23']);
+    });
+
+    it('numeric sequences with step count', function() {
+      match('{0..8..2}', ['0', '2', '4', '6', '8']);
+      match('{1..8..2}', ['1', '3', '5', '7']);
+    });
+
+    it('numeric sequence with negative x / y', function() {
+      match('{3..-2}', ['3', '2', '1', '0', '-1', '-2']);
+    });
+
+    it('alphabetic sequences', function() {
+      match('1{a..b}2{b..c}3', ['1a2b3', '1a2c3', '1b2b3', '1b2c3']);
+      match('{a..b}{b..c}', ['ab', 'ac', 'bb', 'bc']);
+    });
+
+    it('alphabetic sequences with step count', function() {
+      match('{a..k..2}', ['a', 'c', 'e', 'g', 'i', 'k']);
+      match('{b..k..2}', ['b', 'd', 'f', 'h', 'j']);
+    });
+  });
+
+  describe('dollar', function() {
+    it('ignores ${', function() {
+      match('${1..3}', ['${1..3}']);
+      match('${a,b}${c,d}', ['${a,b}${c,d}']);
+      match('x${a,b}x${c,d}x', ['x${a,b}x${c,d}x']);
+    });
+  });
+
+  describe('empty option', function() {
+    it('should support empty sets', function() {
+      match('-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']);
+    });
+  });
+
+  describe('negative increments', function() {
+    it('should support negative steps', function() {
+      match('{3..1}', ['3', '2', '1']);
+      match('{10..8}', ['10', '9', '8']);
+      match('{10..08}', ['10', '09', '08']);
+      match('{c..a}', ['c', 'b', 'a']);
+
+      match('{4..0..2}', ['4', '2', '0']);
+      match('{4..0..-2}', ['4', '2', '0']);
+      match('{e..a..2}', ['e', 'c', 'a']);
+    });
+  });
+
+  describe('nested', function() {
+    it('should support nested sets', function() {
+      match('{a,b{1..3},c}', ['a', 'b1', 'b2', 'b3', 'c']);
+      match('{{A..Z},{a..z}}', [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ].sort());
+      match('ppp{,config,oe{,conf}}', ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']);
+    });
+  });
+
+  describe('order', function() {
+    it('should expand in given order', function() {
+      match('a{d,c,b}e', ['ade', 'ace', 'abe']);
+    });
+  });
+
+  describe('pad', function() {
+    it('should support padding', function() {
+      match('{9..11}', ['9', '10', '11']);
+      match('{09..11}', ['09', '10', '11']);
+    });
+  });
+});
+
diff --git a/test/braces.compile.js b/test/braces.compile.js
new file mode 100644
index 0000000..03a5be6
--- /dev/null
+++ b/test/braces.compile.js
@@ -0,0 +1,19 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+describe('.compile', function() {
+  it('should return an object', function() {
+    var res = braces.compile('a/{b,c}/d');
+    assert(res);
+    assert.equal(typeof res, 'object');
+  });
+
+  it('should return output as an array', function() {
+    var res = braces.compile('a/{b,c}/d');
+    assert(Array.isArray(res.output));
+    assert.deepEqual(res.output, ['a/(b|c)/d']);
+  });
+});
diff --git a/test/braces.js b/test/braces.js
new file mode 100644
index 0000000..9d4a1c2
--- /dev/null
+++ b/test/braces.js
@@ -0,0 +1,58 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  assert.deepEqual(braces(pattern, options), expected);
+}
+
+describe('braces', function() {
+  it('should return an array', function() {
+    assert(Array.isArray(braces('{a,b}')));
+  });
+
+  it('should return an optimized string by default', function() {
+    match('a/{b,c}/d', ['a/(b|c)/d']);
+  });
+
+  it('should return an expanded array if defined on options', function() {
+    match('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
+  });
+
+  it('should optimize an array of patterns', function() {
+    match(['a/{b,c}/d', 'x/{foo,bar}/z'], ['a/(b|c)/d', 'x/(foo|bar)/z']);
+  });
+
+  it('should expand an array of patterns', function() {
+    var actual = braces(['a/{b,c}/d', 'a/{b,c}/d']);
+    assert.deepEqual(actual, ['a/(b|c)/d', 'a/(b|c)/d']);
+  });
+
+  it('should not uniquify by default', function() {
+    var actual = braces(['a/{b,c}/d', 'a/{b,c}/d']);
+    assert.deepEqual(actual, ['a/(b|c)/d', 'a/(b|c)/d']);
+  });
+
+  it('should uniquify when `options.nodupes` is true', function() {
+    var actual = braces(['a/{b,c}/d', 'a/{b,c}/d'], {nodupes: true});
+    assert.deepEqual(actual, ['a/(b|c)/d']);
+  });
+
+  it('should expand ranges', function() {
+    match('a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'], {expand: true});
+  });
+
+  it('should expand ranges that are nested in a set', function() {
+    match('a{b,c,{1..5}}e', ['abe', 'ace', 'a1e', 'a2e', 'a3e', 'a4e', 'a5e'], {expand: true});
+  });
+
+  it('should not expand ranges when they are just characters in a set', function() {
+    match('a{b,c,1..5}e', ['abe', 'ace', 'a1..5e'], {expand: true});
+    match('a{/../}e', ['a/e'], {expand: true});
+    match('a{/../,z}e', ['a/../e', 'aze'], {expand: true});
+    match('a{b,c/*/../d}e', ['abe', 'ac/*/../de'], {expand: true});
+    match('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd'], {expand: true});
+  });
+});
diff --git a/test/braces.parse.js b/test/braces.parse.js
new file mode 100644
index 0000000..7e94f44
--- /dev/null
+++ b/test/braces.parse.js
@@ -0,0 +1,18 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+describe('.parse', function() {
+  it('should return an AST object', function() {
+    var ast = braces.parse('a/{b,c}/d');
+    assert(ast);
+    assert.equal(typeof ast, 'object');
+  });
+
+  it('should have an array of nodes', function() {
+    var ast = braces.parse('a/{b,c}/d');
+    assert(Array.isArray(ast.nodes));
+  });
+});
diff --git a/test/expanded.integration.js b/test/expanded.integration.js
new file mode 100644
index 0000000..b855ffe
--- /dev/null
+++ b/test/expanded.integration.js
@@ -0,0 +1,27 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+describe('integration', function() {
+  it('should work with dots in file paths', function() {
+    match('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
+    match('../{2..10..2}/../foo', ['../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo']);
+    match('../{1..3}/../{a,b,c}/foo', ['../1/../a/foo', '../2/../a/foo', '../3/../a/foo', '../1/../b/foo', '../2/../b/foo', '../3/../b/foo', '../1/../c/foo', '../2/../c/foo', '../3/../c/foo']);
+    match('./{a..z..3}/', ['./a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/']);
+    match('./{"x,y"}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
+  });
+
+  it('should expand a complex combination of ranges and sets:', function() {
+    match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', 'a/ [...]
+  });
+
+  it('should expand complex sets and ranges in `bash` mode:', function() {
+    match('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/y/c{d}e']);
+  });
+});
diff --git a/test/expanded.ranges.js b/test/expanded.ranges.js
new file mode 100644
index 0000000..cd32ea0
--- /dev/null
+++ b/test/expanded.ranges.js
@@ -0,0 +1,187 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected) {
+  var actual = braces.expand(pattern).sort();
+  assert.deepEqual(actual, expected.sort());
+}
+
+describe('expanded ranges', function() {
+  describe('escaping / invalid ranges', function() {
+    it('should not try to expand ranges with decimals', function() {
+      match('{1.1..2.1}', ['{1.1..2.1}']);
+      match('{1.1..~2.1}', ['{1.1..~2.1}']);
+    });
+
+    it('should escape invalid ranges:', function() {
+      match('{1..0f}', ['{1..0f}']);
+      match('{1..10..ff}', ['{1..10..ff}']);
+      match('{1..10.f}', ['{1..10.f}']);
+      match('{1..10f}', ['{1..10f}']);
+      match('{1..20..2f}', ['{1..20..2f}']);
+      match('{1..20..f2}', ['{1..20..f2}']);
+      match('{1..2f..2}', ['{1..2f..2}']);
+      match('{1..ff..2}', ['{1..ff..2}']);
+      match('{1..ff}', ['{1..ff}']);
+      match('{1.20..2}', ['{1.20..2}']);
+    });
+
+    it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
+      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+    });
+
+    it('should not expand quoted strings.', function() {
+      match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+      match('{"x,x"}', ['{x,x}']);
+    });
+
+    it('should escaped outer braces in nested non-sets', function() {
+      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+    });
+
+    it('should escape imbalanced braces', function() {
+      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+      match('abc{', ['abc{']);
+      match('{abc{', ['{abc{']);
+      match('{abc', ['{abc']);
+      match('}abc', ['}abc']);
+      match('ab{c', ['ab{c']);
+      match('ab{c', ['ab{c']);
+      match('{{a,b}', ['{a', '{b']);
+      match('{a,b}}', ['a}', 'b}']);
+      match('abcd{efgh', ['abcd{efgh']);
+      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      match('f{x,y{{g}h', ['f{x,y{{g}h']);
+      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+      match('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']);
+      match('f{x,y{}g}h', ['fxh', 'fy{}gh']);
+      match('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
+    });
+  });
+
+  describe('positive numeric ranges', function() {
+    it('should expand numeric ranges', function() {
+      match('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
+      match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+      match('x{3..3}y', ['x3y']);
+      match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      match('{1..3}', ['1', '2', '3']);
+      match('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
+      match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+      match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+      match('{3..3}', ['3']);
+      match('{5..8}', ['5', '6', '7', '8']);
+    });
+  });
+
+  describe('negative ranges', function() {
+    it('should expand ranges with negative numbers', function() {
+      match('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
+      match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+      match('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
+      match('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
+    });
+  });
+
+  describe('alphabetical ranges', function() {
+    it('should expand alphabetical ranges', function() {
+      match('{a..F}', ['F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+      match('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
+      match('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
+      match('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+      match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+      match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+      match('{a..e}', ['a', 'b', 'c', 'd', 'e']);
+      match('{A..E}', ['A', 'B', 'C', 'D', 'E']);
+      match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+      match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+      match('{E..A}', ['E', 'D', 'C', 'B', 'A']);
+      match('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+      match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+      match('{f..f}', ['f']);
+    });
+
+    it('should expand multiple ranges:', function() {
+      match('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']);
+    });
+  });
+
+  describe('combo', function() {
+    it('should expand numerical ranges - positive and negative', function() {
+      match('a{01..05}b', ['a01b', 'a02b', 'a03b', 'a04b', 'a05b' ]);
+      match('0{1..9}/{10..20}', ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16',  [...]
+      match('{-10..10}', ['-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ]);
+    });
+  });
+
+  // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
+  describe('large numbers', function() {
+    it('should expand large numbers', function() {
+      match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+      match('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']);
+    });
+  });
+
+  describe('steps > positive ranges', function() {
+    it('should expand ranges using steps:', function() {
+      match('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      match('{1..10..2}', ['1', '3', '5', '7', '9']);
+      match('{1..20..20}', ['1']);
+      match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+      match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+      match('{10..1..2}', ['10', '8', '6', '4', '2']);
+      match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+      match('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      match('{2..10..2}', ['2', '4', '6', '8', '10']);
+      match('{2..10..3}', ['2', '5', '8']);
+      match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+    });
+
+    it('should expand positive ranges with negative steps:', function() {
+      match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+    });
+  });
+
+  describe('steps > negative ranges', function() {
+    it('should expand negative ranges using steps:', function() {
+      match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+      match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+      match('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
+      match('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+      match('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
+      match('{-2..-10..3}', ['-2', '-5', '-8']);
+      match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+      match('{10..1..-2}', ['2', '4', '6', '8', '10']);
+      match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    });
+  });
+
+  describe('steps > alphabetical ranges', function() {
+    it('should expand alpha ranges with steps', function() {
+      match('{a..e..2}', ['a', 'c', 'e']);
+      match('{E..A..2}', ['E', 'C', 'A']);
+      match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+      match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+      match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+    });
+
+    it('should expand alpha ranges with negative steps', function() {
+      match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+    });
+  });
+
+  describe('padding', function() {
+    it('unwanted zero-padding -- fixed post-bash-4.0', function() {
+      match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+      match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+      match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+    });
+  });
+});
diff --git a/test/expanded.sets.js b/test/expanded.sets.js
new file mode 100644
index 0000000..0d1d6bb
--- /dev/null
+++ b/test/expanded.sets.js
@@ -0,0 +1,204 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected) {
+  assert.deepEqual(braces.expand(pattern).sort(), expected.sort());
+}
+
+describe('expanded sets', function() {
+  describe('invalid sets', function() {
+    it('should handle invalid sets:', function() {
+      match('{0..10,braces}', ['0..10', 'braces']);
+      match('{1..10,braces}', ['1..10', 'braces']);
+    });
+  });
+
+  describe('escaping', function() {
+    it('should not expand escaped braces', function() {
+      match('y{\\},a}x', ['y}x', 'yax']);
+      match('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']);
+      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      match('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
+      match('a/\\{x,y}/cde', ['a/{x,y}/cde']);
+      match('abcd{efgh', ['abcd{efgh']);
+      match('{abc}', ['{abc}']);
+      match('{x,y,\\{a,b,c\\}}', ['x', 'y', '{a', 'b', 'c}']);
+      match('{x,y,{a,b,c\\}}', ['{x,y,a', '{x,y,b', '{x,y,c}']);
+      match('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
+      match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+    });
+
+    it('should handle empty braces', function() {
+      match('{ }', ['{ }']);
+      match('{', ['{']);
+      match('{}', ['{}']);
+      match('}', ['}']);
+    });
+
+    it('should handle empty sets', function() {
+      match('{ }', ['{ }']);
+      match('{', ['{']);
+      match('{}', ['{}']);
+      match('}', ['}']);
+    });
+
+    it('should escape braces when only one value is defined', function() {
+      match('a{b}c', ['a{b}c']);
+      match('a/b/c{d}e', ['a/b/c{d}e']);
+    });
+
+    it('should escape closing braces when open is not defined', function() {
+      match('{a,b}c,d}', ['ac,d}', 'bc,d}']);
+    });
+
+    it('should not expand braces in sets with es6/bash-like variables', function() {
+      match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+      match('a${b}c', ['a${b}c']);
+      match('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
+      match('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
+    });
+
+    it('should not expand escaped commas.', function() {
+      match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+      match('a{b\\,c}d', ['a{b,c}d']);
+      match('{abc\\,def}', ['{abc,def}']);
+      match('{abc\\,def,ghi}', ['abc,def', 'ghi']);
+      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+    });
+
+    it('should return sets with escaped commas', function() {
+    });
+
+    it('should not expand escaped braces.', function() {
+      match('{a,b\\}c,d}', ['a', 'b}c', 'd']);
+      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      match('a/{z,\\{a,b,c,d,e}/d', ['a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d']);
+      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
+      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
+    });
+
+    it('should not expand escaped braces or commas.', function() {
+      match('{x\\,y,\\{abc\\},trie}', ['{abc}', 'trie', 'x,y']);
+    });
+  });
+
+  describe('multipliers', function() {
+    it('should support multipliers', function() {
+      match('{{d,d},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      match('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      match('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
+      match('a{,,}', ['a', 'a', 'a']);
+      match('a{,}', ['a', 'a']);
+      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a{,}{,}', ['a', 'a', 'a', 'a']);
+      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      match('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      match('{,}', []);
+      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      match('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
+      match('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      match('a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']);
+      match('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
+      match('a{,,}', ['a', 'a', 'a']);
+      match('a{,}', ['a', 'a']);
+      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('a{,}{,}', ['a', 'a', 'a', 'a']);
+      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      match('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      match('{,}', []);
+      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      match('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
+    });
+  });
+
+  describe('set expansion', function() {
+    it('should support sequence brace operators', function() {
+      match('{a,b,c}', ['a', 'b', 'c']);
+      match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    });
+
+    it('should support sequence braces with leading characters', function() {
+      match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex']);
+      match('ff{c,b,a}', ['ffa', 'ffb', 'ffc']);
+    });
+
+    it('should support sequence braces with trailing characters', function() {
+      match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+      match('{l,n,m}xyz', ['lxyz', 'mxyz', 'nxyz']);
+    });
+
+    it('should support sequence braces with trailing characters', function() {
+      match('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      match('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
+      match('{{1..10..2},braces}', ['1', '3', '5', '7', '9', 'braces']);
+      match('{{1..10},braces}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
+    });
+
+    it('should support nested sequence braces with trailing characters', function() {
+      match('x{{0..10},braces}y', ['xbracesy', 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+    });
+
+    it('should expand multiple sets', function() {
+      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
+      match('a{b,c}d{e,f}g', ['abdeg', 'abdfg', 'acdeg', 'acdfg']);
+      match('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt']);
+    });
+
+    it('should expand nested sets', function() {
+      match('{{d,d},e}{}', ['d{}', 'd{}', 'e{}']);
+      match('{{d,d},e}a', ['da', 'da', 'ea']);
+      match('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
+      match('{d,d,{d,d},{e,e}}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      match('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
+      match('{{d,d},e}', ['d', 'd', 'e']);
+      match('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
+      match('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
+      match('a{{x,y},z}b', ['axb', 'ayb', 'azb']);
+      match('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
+      match('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'acex/z', 'ahx/z']);
+      match('a{b,c{d,e},h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz']);
+      match('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']);
+      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+    });
+
+    it('should expand with globs.', function() {
+      match('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
+      match('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
+      match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt']);
+      match('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
+    });
+  });
+
+  describe('commas', function() {
+    it('should work with leading and trailing commas.', function() {
+      match('a{b,}c', ['abc', 'ac']);
+      match('a{,b}c', ['abc', 'ac']);
+      match('{{a,b},a}c', ['ac', 'ac', 'bc']);
+      match('{{a,b},}c', ['ac', 'bc', 'c']);
+      match('a{{a,b},}c', ['aac', 'abc', 'ac']);
+    });
+  });
+
+  describe('spaces', function() {
+    it('should handle spaces', function() {
+      match('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16',  [...]
+      match('a{ ,c{d, },h}x', ['acdx', 'ac x', 'ahx', 'a x']);
+      match('a{ ,c{d, },h} ', ['a  ', 'ac  ', 'acd ', 'ah ']);
+
+      // see https://github.com/jonschlinkert/micromatch/issues/66
+      match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ]);
+
+      // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`,
+      // That makes sense in Bash, since ' ' is a separator, but not here.
+      match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+    });
+  });
+});
diff --git a/test/minimatch.js b/test/minimatch.js
new file mode 100644
index 0000000..ba935c8
--- /dev/null
+++ b/test/minimatch.js
@@ -0,0 +1,28 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+/**
+ * minimatch v3.0.3 unit tests <https://github.com/isaacs/minimatch>
+ */
+
+describe('brace expansion', function() {
+  var units = [
+    ['a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']],
+    ['a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'] ],
+    ['a{b}c', ['a{b}c']],
+    ['a{00..05}b', ['a00b', 'a01b', 'a02b', 'a03b', 'a04b', 'a05b'] ],
+    ['z{a,b},c}d', ['za,c}d', 'zb,c}d']],
+    ['z{a,b{,c}d', ['z{a,bcd', 'z{a,bd']],
+    ['a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']],
+    ['a{b{c{d,e}f{x,y}}g}h', ['a{b{cdfx}g}h', 'a{b{cdfy}g}h', 'a{b{cefx}g}h', 'a{b{cefy}g}h'] ],
+    ['a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh'] ]
+  ];
+
+  units.forEach(function(unit) {
+    it('should expand: ' + unit[0], function() {
+      assert.deepEqual(braces.expand(unit[0]), unit[1], unit[0]);
+    });
+  });
+});
diff --git a/test/multiples.js b/test/multiples.js
new file mode 100644
index 0000000..44869ed
--- /dev/null
+++ b/test/multiples.js
@@ -0,0 +1,60 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+describe('multiples', function() {
+  var patterns = [
+    ['-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']],
+    ['-v{,,,,}{,}', ['-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v', '-v']],
+    ['a/b{,}', ['a/b', 'a/b']],
+    ['a/{,}/b', ['a//b', 'a//b']],
+    ['a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']],
+    ['a/{a,b,{,}{,}{,},c}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b', 'a/c/b']],
+    ['a/{a,b,{,}{,}{,}}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b']],
+    ['a/{b,cz{,}}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']],
+    ['a/{b,cz}{,}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']],
+    ['a/{b,c{,}}', ['a/b', 'a/c', 'a/c']],
+    ['a/{b,c{,}}/{,}', ['a/b/', 'a/b/', 'a/c/', 'a/c/', 'a/c/', 'a/c/']],
+    ['a/{b,c}/{,}', ['a/b/', 'a/b/', 'a/c/', 'a/c/']],
+    ['a/{b,c}{,}/d{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d']],
+    ['a/{b,c}{,}/{d,e{,}}', ['a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']],
+    ['a/{b,c}{,}/{d,e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']],
+    ['a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']],
+    ['a/{c,d}/{x,y{,}}/e', ['a/c/x/e', 'a/c/y/e', 'a/c/y/e', 'a/d/x/e', 'a/d/y/e', 'a/d/y/e']],
+    ['a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']],
+    ['a{,,,,,}', ['a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,,,,,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,,,,,}{,,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,,,,,}{,,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,,,,}', ['a', 'a', 'a', 'a', 'a']],
+    ['a{,,,}', ['a', 'a', 'a', 'a']],
+    ['a{,,}', ['a', 'a', 'a']],
+    ['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/b', 'a/b', 'a/b']],
+    ['a{,,}{,}', ['a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,}', ['a', 'a']],
+    ['a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']],
+    ['a{,}b', ['ab', 'ab']],
+    ['a{,}{,}', ['a', 'a', 'a', 'a']],
+    ['a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']],
+    ['a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']],
+    ['one/{a{,}{,}}/{b/c{,,}{,}{,,}{,}}/two', ['one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one/{a}/{b/c}/two', 'one [...]
+    ['{,}', []],
+    ['{,}a/{,}', ['a/', 'a/', 'a/', 'a/']],
+    ['{,}{,}', []],
+    ['{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']],
+    ['{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']],
+    ['{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']]
+  ];
+
+  patterns.forEach(function(pattern) {
+    it('should expand: ' + pattern[0], function() {
+      match(pattern[0], pattern[1]);
+    });
+  });
+});
diff --git a/test/optimized.js b/test/optimized.js
new file mode 100644
index 0000000..070c905
--- /dev/null
+++ b/test/optimized.js
@@ -0,0 +1,350 @@
+/*!
+ * braces <https://github.com/jonschlinkert/braces>
+ *
+ * Copyright (c) 2014-2016, Jon Schlinkert.
+ * Licensed under the MIT License
+ */
+
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  assert.deepEqual(braces(pattern, options), expected);
+}
+
+describe('optimized', function() {
+  describe('sets', function() {
+    describe('invalid sets', function() {
+      it('should handle invalid sets:', function() {
+        match('{0..10,braces}', ['(0..10|braces)']);
+        match('{1..10,braces}', ['(1..10|braces)']);
+      });
+    });
+
+    describe('escaping', function() {
+      it('should not expand escaped braces', function() {
+        match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+        match('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
+        match('a/\\{x,y}/cde', ['a/{x,y}/cde']);
+        match('abcd{efgh', ['abcd{efgh']);
+        match('{abc}', ['{abc}']);
+        match('{x,y,\\{a,b,c\\}}', ['(x|y|{a|b|c})']);
+        match('{x,y,{a,b,c\\}}', ['{x,y,(a|b|c})']);
+        match('{x,y,{abc},trie}', ['(x|y|{abc}|trie)']);
+        match('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
+      });
+
+      it('should handle spaces', function() {
+        // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`,
+        // That makes sense in Bash, since ' ' is a separator, but not here.
+        match('foo {1,2} bar', ['foo (1|2) bar']);
+      });
+
+      it('should handle empty braces', function() {
+        match('{ }', ['{ }']);
+        match('{', ['{']);
+        match('{}', ['{}']);
+        match('}', ['}']);
+      });
+
+      it('should escape braces when only one value is defined', function() {
+        match('a{b}c', ['a{b}c']);
+        match('a/b/c{d}e', ['a/b/c{d}e']);
+      });
+
+      it('should not expand braces in sets with es6/bash-like variables', function() {
+        match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+        match('a${b}c', ['a${b}c']);
+        match('a/{${b},c}/d', ['a/(${b}|c)/d']);
+        match('a${b,d}/{foo,bar}c', ['a${b,d}/(foo|bar)c']);
+      });
+
+      it('should not expand escaped commas.', function() {
+        match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+        match('a{b\\,c}d', ['a{b,c}d']);
+        match('{abc\\,def}', ['{abc,def}']);
+        match('{abc\\,def,ghi}', ['(abc,def|ghi)']);
+        match('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
+      });
+
+      it('should return sets with escaped commas', function() {
+        match('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
+      });
+
+      it('should not expand escaped braces.', function() {
+        match('{a,b\\}c,d}', ['(a|b}c|d)']);
+        match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+        match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
+        match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
+        match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+      });
+
+      it('should not expand escaped braces or commas.', function() {
+        match('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
+      });
+    });
+
+    describe('set expansion', function() {
+      it('should support sequence brace operators', function() {
+        match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']);
+        match('ff{c,b,a}', ['ff(c|b|a)']);
+        match('f{d,e,f}g', ['f(d|e|f)g']);
+        match('x{{0..10},braces}y', ['x(([0-9]|10)|braces)y']);
+        match('{1..10}', ['([1-9]|10)']);
+        match('{a,b,c}', ['(a|b|c)']);
+        match('{braces,{0..10}}', ['(braces|([0-9]|10))']);
+        match('{l,n,m}xyz', ['(l|n|m)xyz']);
+        match('{{0..10},braces}', ['(([0-9]|10)|braces)']);
+        match('{{1..10..2},braces}', ['((1|3|5|7|9)|braces)']);
+        match('{{1..10},braces}', ['(([1-9]|10)|braces)']);
+      });
+
+      it('should expand multiple sets', function() {
+        match('a/{a,b}/{c,d}/e', ['a/(a|b)/(c|d)/e']);
+        match('a{b,c}d{e,f}g', ['a(b|c)d(e|f)g']);
+        match('a/{x,y}/c{d,e}f.{md,txt}', ['a/(x|y)/c(d|e)f.(md|txt)']);
+      });
+
+      it('should expand nested sets', function() {
+        match('{a,b}{{a,b},a,b}', ['(a|b)((a|b)|a|b)']);
+        match('a{b,c{d,e}f}g', ['a(b|c(d|e)f)g']);
+        match('a{{x,y},z}b', ['a((x|y)|z)b']);
+        match('f{x,y{g,z}}h', ['f(x|y(g|z))h']);
+        match('a{b,c}{d,e}/hx/z', ['a(b|c)(d|e)/hx/z']);
+        match('a{b,c{d,e},h}x/z', ['a(b|c(d|e)|h)x/z']);
+        match('a{b,c{d,e},h}x{y,z}', ['a(b|c(d|e)|h)x(y|z)']);
+        match('a{b,c{d,e},{f,g}h}x{y,z}', ['a(b|c(d|e)|(f|g)h)x(y|z)']);
+        match('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
+      });
+
+      it('should expand not modify non-brace characters', function() {
+        match('a/b/{d,e}/*.js', ['a/b/(d|e)/*.js']);
+        match('a/**/c/{d,e}/f*.js', ['a/**/c/(d|e)/f*.js']);
+        match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/(d|e)/f*.(md|txt)']);
+      });
+    });
+
+    describe('commas', function() {
+      it('should work with leading and trailing commas.', function() {
+        match('a{b,}c', ['a(b|)c']);
+        match('a{,b}c', ['a(|b)c']);
+      });
+    });
+
+    describe('spaces', function() {
+      it('should handle spaces', function() {
+        match('0{1..9} {10..20}', ['0([1-9]) (1[0-9]|20)']);
+        match('a{ ,c{d, },h}x', ['a( |c(d| )|h)x']);
+        match('a{ ,c{d, },h} ', ['a( |c(d| )|h) ']);
+
+        // see https://github.com/jonschlinkert/micromatch/issues/66
+        match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']);
+      });
+    });
+  });
+
+  /**
+   * Ranges
+   */
+
+  describe('ranges', function() {
+    describe('escaping / invalid ranges', function() {
+      it('should not try to expand ranges with decimals', function() {
+        match('{1.1..2.1}', ['{1.1..2.1}']);
+        match('{1.1..~2.1}', ['{1.1..~2.1}']);
+      });
+
+      it('should escape invalid ranges:', function() {
+        match('{1..0f}', ['{1..0f}']);
+        match('{1..10..ff}', ['{1..10..ff}']);
+        match('{1..10.f}', ['{1..10.f}']);
+        match('{1..10f}', ['{1..10f}']);
+        match('{1..20..2f}', ['{1..20..2f}']);
+        match('{1..20..f2}', ['{1..20..f2}']);
+        match('{1..2f..2}', ['{1..2f..2}']);
+        match('{1..ff..2}', ['{1..ff..2}']);
+        match('{1..ff}', ['{1..ff}']);
+        match('{1..f}', ['([1-f])']);
+        match('{1.20..2}', ['{1.20..2}']);
+      });
+
+      it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
+        match('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
+        match('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
+      });
+
+      it('should not expand quoted strings.', function() {
+        match('{"klklkl"}{1,2,3}', ['{klklkl}(1|2|3)']);
+        match('{"x,x"}', ['{x,x}']);
+      });
+
+      it('should escaped outer braces in nested non-sets', function() {
+        match('{a-{b,c,d}}', ['{a-(b|c|d)}']);
+        match('{a,{a-{b,c,d}}}', ['(a|{a-(b|c|d)})']);
+      });
+
+      it('should escape imbalanced braces', function() {
+        match('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
+        match('abc{', ['abc{']);
+        match('{abc{', ['{abc{']);
+        match('{abc', ['{abc']);
+        match('}abc', ['}abc']);
+        match('ab{c', ['ab{c']);
+        match('{{a,b}', ['{(a|b)']);
+        match('{a,b}}', ['(a|b)}']);
+        match('abcd{efgh', ['abcd{efgh']);
+        match('a{b{c{d,e}f}g}h', ['a(b(c(d|e)f)g)h']);
+        match('f{x,y{{g,z}}h}', ['f(x|y((g|z))h)']);
+        match('z{a,b},c}d', ['z(a|b),c}d']);
+        match('a{b{c{d,e}f{x,y{{g}h', ['a{b{c(d|e)f{x,y{{g}h']);
+        match('f{x,y{{g}h', ['f{x,y{{g}h']);
+        match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+        match('a{b{c{d,e}f{x,y{}g}h', ['a{b{c(d|e)f(x|y{}g)h']);
+        match('f{x,y{}g}h', ['f(x|y{}g)h']);
+        match('z{a,b{,c}d', ['z{a,b(|c)d']);
+      });
+    });
+
+    describe('positive numeric ranges', function() {
+      it('should expand numeric ranges', function() {
+        match('a{0..3}d', ['a([0-3])d']);
+        match('x{10..1}y', ['x([1-9]|10)y']);
+        match('x{3..3}y', ['x3y']);
+        match('{1..10}', ['([1-9]|10)']);
+        match('{1..3}', ['([1-3])']);
+        match('{1..9}', ['([1-9])']);
+        match('{10..1}', ['([1-9]|10)']);
+        match('{10..1}y', ['([1-9]|10)y']);
+        match('{3..3}', ['3']);
+        match('{5..8}', ['([5-8])']);
+      });
+    });
+
+    describe('negative ranges', function() {
+      it('should expand ranges with negative numbers', function() {
+        match('{-1..-10}', ['(-[1-9]|-10)']);
+        match('{-10..-1}', ['(-[1-9]|-10)']);
+        match('{-20..0}', ['(-[1-9]|-1[0-9]|-20|0)']);
+        match('{0..-5}', ['(-[1-5]|0)']);
+        match('{9..-4}', ['(-[1-4]|[0-9])']);
+      });
+    });
+
+    describe('alphabetical ranges', function() {
+      it('should expand alphabetical ranges', function() {
+        match('0{1..9}/{10..20}', ['0([1-9])/(1[0-9]|20)']);
+        match('0{a..d}0', ['0([a-d])0']);
+        match('a/{b..d}/e', ['a/([b-d])/e']);
+        match('{1..f}', ['([1-f])']);
+        match('{a..A}', ['([A-a])']);
+        match('{A..a}', ['([A-a])']);
+        match('{a..e}', ['([a-e])']);
+        match('{A..E}', ['([A-E])']);
+        match('{a..f}', ['([a-f])']);
+        match('{a..z}', ['([a-z])']);
+        match('{E..A}', ['([A-E])']);
+        match('{f..1}', ['([1-f])']);
+        match('{f..a}', ['([a-f])']);
+        match('{f..f}', ['f']);
+      });
+
+      it('should expand multiple ranges:', function() {
+        match('a/{b..d}/e/{f..h}', ['a/([b-d])/e/([f-h])']);
+      });
+    });
+
+    describe('combo', function() {
+      it('should expand numerical ranges - positive and negative', function() {
+        match('{-10..10}', ['(-[1-9]|-?10|[0-9])']);
+      });
+    });
+
+    // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
+    describe('large numbers', function() {
+      it('should expand large numbers', function() {
+        match('{2147483645..2147483649}', ['(214748364[5-9])']);
+        match('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']);
+      });
+    });
+
+    describe('steps > positive ranges', function() {
+      it('should expand ranges using steps:', function() {
+        match('{1..10..1}', ['([1-9]|10)']);
+        match('{1..10..2}', ['(1|3|5|7|9)']);
+        match('{1..20..20}', ['1']);
+        match('{1..20..20}', ['1']);
+        match('{1..20..20}', ['1']);
+        match('{1..20..2}', ['(1|3|5|7|9|11|13|15|17|19)']);
+        match('{10..0..2}', ['(10|8|6|4|2|0)']);
+        match('{10..1..2}', ['(10|8|6|4|2)']);
+        match('{100..0..5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
+        match('{2..10..1}', ['([2-9]|10)']);
+        match('{2..10..2}', ['(2|4|6|8|10)']);
+        match('{2..10..3}', ['(2|5|8)']);
+        match('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
+      });
+
+      it('should expand positive ranges with negative steps:', function() {
+        match('{10..0..-2}', ['(10|8|6|4|2|0)']);
+      });
+    });
+
+    describe('steps > negative ranges', function() {
+      it('should expand negative ranges using steps:', function() {
+        match('{-1..-10..-2}', ['(-(1|3|5|7|9))']);
+        match('{-1..-10..2}', ['(-(1|3|5|7|9))']);
+        match('{-10..-2..2}', ['(-(10|8|6|4|2))']);
+        match('{-2..-10..1}', ['(-[2-9]|-10)']);
+        match('{-2..-10..2}', ['(-(2|4|6|8|10))']);
+        match('{-2..-10..3}', ['(-(2|5|8))']);
+        match('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
+        match('{-9..9..3}', ['(0|3|6|9|-(9|6|3))']);
+        match('{10..1..-2}', ['(10|8|6|4|2)']);
+        match('{100..0..-5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
+      });
+    });
+
+    describe('steps > alphabetical ranges', function() {
+      it('should expand alpha ranges with steps', function() {
+        match('{a..e..2}', ['(a|c|e)']);
+        match('{E..A..2}', ['(E|C|A)']);
+        match('{a..z}', ['([a-z])']);
+        match('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
+        match('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
+      });
+
+      it('should expand alpha ranges with negative steps', function() {
+        match('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
+      });
+    });
+
+    describe('padding', function() {
+      it('unwanted zero-padding -- fixed post-bash-4.0', function() {
+        match('{10..0..2}', ['(10|8|6|4|2|0)']);
+        match('{10..0..-2}', ['(10|8|6|4|2|0)']);
+        match('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
+      });
+    });
+  });
+
+  describe('integration', function() {
+    it('should work with dots in file paths', function() {
+      match('../{1..3}/../foo', ['../([1-3])/../foo']);
+      match('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo']);
+      match('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo']);
+      match('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/']);
+      match('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+    });
+
+    it('should expand a complex combination of ranges and sets:', function() {
+      match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']);
+    });
+
+    it('should expand complex sets and ranges in `bash` mode:', function() {
+      match('a/{x,{1..5},y}/c{d}e', ['a/(x|([1-5])|y)/c{d}e']);
+    });
+  });
+});
diff --git a/test/options.js b/test/options.js
new file mode 100644
index 0000000..e790ce0
--- /dev/null
+++ b/test/options.js
@@ -0,0 +1,73 @@
+/*!
+ * braces <https://github.com/jonschlinkert/braces>
+ *
+ * Copyright (c) 2014-2016, Jon Schlinkert.
+ * Licensed under the MIT License
+ */
+
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  assert.deepEqual(braces(pattern, options).sort(), expected.sort());
+}
+
+describe('options', function() {
+  describe('options.expand', function() {
+    it('should expand braces when `options.expand` is true', function() {
+      match('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
+    });
+  });
+
+  describe('options.unescape', function() {
+    it('should remove backslashes from escaped brace characters', function() {
+      match('{a,b\\}c,d}', ['(a|b}c|d)']);
+      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
+      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
+      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+    });
+
+    it('should not remove backslashes when `options.unescape` is false', function() {
+      match('{a,b\\}c,d}', ['(a|b\\}c|d)'], {unescape: false});
+      match('\\{a,b,c,d,e}', ['\\{a,b,c,d,e}'], {unescape: false});
+      match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|\\{a|b|c|d|e)/d'], {unescape: false});
+      match('a/\\{b,c}/{d,e}/f', ['a/\\{b,c}/(d|e)/f'], {unescape: false});
+      match('./\\{x,y}/{a..z..3}/', ['./\\{x,y}/(a|d|g|j|m|p|s|v|y)/'], {unescape: false});
+    });
+  });
+
+  describe('options.nodupes', function() {
+    it('should not remove duplicates by default', function() {
+      match('a/{b,b,b}/c', ['a/b/c', 'a/b/c', 'a/b/c'], {expand: true});
+    });
+
+    it('should remove duplicates when `options.nodupes` is true', function() {
+      match('a/{b,b,b}/c', ['a/b/c'], {expand: true, nodupes: true});
+    });
+  });
+
+  describe('options.optimize', function() {
+    it('should optimize braces when `options.optimize` is true', function() {
+      match('a/{b,c}/d', ['a/(b|c)/d'], {optimize: true});
+    });
+  });
+
+  describe('options.quantifiers:', function() {
+    it('should not expand regex quantifiers when `options.quantifiers` is true', function() {
+      match('a{2}c', ['a{2}c']);
+      match('a{2}c', ['a{2}c'], {quantifiers: true});
+      match('a{2,}c', ['a{2,}c'], {quantifiers: true});
+      match('a{,2}c', ['a{,2}c'], {quantifiers: true});
+      match('a{2,3}c', ['a{2,3}c'], {quantifiers: true});
+    });
+
+    it('should expand non-quantifiers when `options.quantifiers` is true', function() {
+      match('a{2}c/{x,y}/z', ['a{2}c/(x|y)/z'], {quantifiers: true});
+      match('a{2}c/{x,y}/z', ['a{2}c/x/z', 'a{2}c/y/z'], {quantifiers: true, expand: true});
+    });
+  });
+});
diff --git a/test/regression-1.8.5.js b/test/regression-1.8.5.js
new file mode 100644
index 0000000..a0d4e5f
--- /dev/null
+++ b/test/regression-1.8.5.js
@@ -0,0 +1,463 @@
+'use strict';
+
+var assert = require('assert');
+var braces = require('..');
+
+function match(pattern, expected, options) {
+  options = options || {};
+  var fn = braces;
+  if (options.optimize !== true) {
+    fn = braces.expand;
+  }
+  var actual = fn(pattern, options).sort();
+  assert.deepEqual(actual, expected.sort(), pattern);
+}
+
+describe('braces tests from 1.8.5', function() {
+  it('braces', function() {
+    match('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
+    match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+    match('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
+    match('{abc\\,d,ef}', ['abc,d', 'ef']);
+    match('{abc}', ['{abc}']);
+
+    match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+    match('{x,y,\\{a,b,c}}', ['x}', 'y}', '{a}', 'b}', 'c}']);
+    match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+
+    match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
+
+    match('{}', ['{}']);
+    match('{ }', ['{ }']);
+    match('}', ['}']);
+    match('{', ['{']);
+    match('abcd{efgh', ['abcd{efgh']);
+
+    match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+  });
+
+  it('new sequence brace operators', function() {
+    match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{0..10,braces}', ['0..10', 'braces']);
+    match('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+  });
+
+  it('ranges', function() {
+    match('{3..3}', ['3']);
+    match('x{3..3}y', ['x3y']);
+    match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+    match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+    match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+    match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+    match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+
+    match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+    match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+
+    match('{f..f}', ['f']);
+    match('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '0 [...]
+  });
+
+  it('mixes are incorrectly-formed brace expansions', function() {
+    // the first one is valid, but Bash fails on it
+    match('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+    match('{f..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+  });
+
+  it('do negative numbers work?', function() {
+    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+  });
+
+  it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
+    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+    match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+
+    match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+
+    match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+    match('{"x,x"}', ['{x,x}']);
+  });
+
+  it('numerical ranges with steps', function() {
+    match('{1..10..2}', ['1', '3', '5', '7', '9']);
+    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+
+    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    match('{10..1..2}', ['10', '8', '6', '4', '2']);
+
+    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    match('{1..20..20}', ['1']);
+
+    match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+  });
+
+  it('alpha ranges with steps', function() {
+    match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+    match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+    match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+  });
+
+  it('make sure brace expansion handles ints > 2**31 - 1 using intmax_t', function() {
+    match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+  });
+
+  it('unwanted zero-padding -- fixed post-bash-4.0', function() {
+    match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+    match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+    match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+  });
+
+  it('bad', function() {
+    match('{1..10.f}', ['{1..10.f}']);
+    match('{1..ff}', ['{1..ff}']);
+    match('{1..10..ff}', ['{1..10..ff}']);
+    match('{1.20..2}', ['{1.20..2}']);
+    match('{1..20..f2}', ['{1..20..f2}']);
+    match('{1..20..2f}', ['{1..20..2f}']);
+    match('{1..2f..2}', ['{1..2f..2}']);
+    match('{1..ff..2}', ['{1..ff..2}']);
+    match('{1..ff}', ['{1..ff}']);
+    match('{1..0f}', ['{1..0f}']);
+    match('{1..10f}', ['{1..10f}']);
+    match('{1..10.f}', ['{1..10.f}']);
+    match('{1..10.f}', ['{1..10.f}']);
+  });
+});
+
+describe('bash tests', function() {
+  describe('brace expansion', function() {
+    it('should return an empty array when no braces are found', function() {
+      match('', ['']);
+    });
+
+    it('should expand emty sets', function() {
+      match('a{,}', ['a', 'a']);
+      match('{,}b', ['b', 'b']);
+      match('a{,}b', ['ab', 'ab']);
+      match('a{,}', ['a', 'a']);
+      match('a{,}{,}', ['a', 'a', 'a', 'a']);
+      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+    });
+
+    it('should eliminate dupes in repeated strings', function() {
+      match('a{,}', ['a'], {nodupes: true});
+      match('a{,}{,}', ['a'], {nodupes: true});
+      match('a{,}{,}{,}', ['a'], {nodupes: true});
+      match('{a,b{,}{,}{,}}', ['a', 'b'], {nodupes: true});
+      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
+      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
+    });
+
+    it('should work with no braces', function() {
+      match('abc', ['abc']);
+    });
+
+    it('should work with no commas', function() {
+      match('a{b}c', ['a{b}c']);
+    });
+
+    it('should work with no commas in `bash` mode', function() {
+      match('a{b}c', ['a{b}c']);
+    });
+
+    it('should handle spaces', function() {
+      match('a{ ,c{d, },h}x', ['a x', 'acdx', 'ac x', 'ahx']);
+      match('a{ ,c{d, },h} ', [ 'a  ', 'acd ', 'ac  ', 'ah ' ]);
+
+      // see https://github.com/jonschlinkert/micromatch/issues/66
+      match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', [
+        '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html',
+        '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs'
+      ]);
+    });
+
+    it('should handle empty braces', function() {
+      match('{ }', ['{ }']);
+      match('{}', ['{}']);
+      match('}', ['}']);
+      match('{', ['{']);
+      match('{,}', []);
+    });
+
+    it('should handle imbalanced braces', function() {
+      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+      match('abc{', ['abc{']);
+      match('{abc{', ['{abc{']);
+      match('{abc', ['{abc']);
+      match('}abc', ['}abc']);
+      match('ab{c', ['ab{c']);
+      match('ab{c', ['ab{c']);
+      match('{{a,b}', ['{a', '{b']);
+      match('{a,b}}', ['a}', 'b}']);
+      match('abcd{efgh', ['abcd{efgh']);
+      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      match('f{x,y{{g}h', ['f{x,y{{g}h']);
+      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+      match('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cefxh', 'a{b{cdfy{}gh', 'a{b{cefy{}gh']);
+      match('f{x,y{}g}h', ['fxh', 'fy{}gh']);
+      match('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
+    });
+
+    it('should handle invalid braces in `bash mode`:', function() {
+      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      match('f{x,y{{g}h', ['f{x,y{{g}h']);
+      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+    });
+
+    it('should return invalid braces:', function() {
+      match('{0..10,braces}', ['0..10', 'braces']);
+    });
+
+    it('should not expand quoted strings.', function() {
+      match('{"x,x"}', ['{x,x}']);
+      match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+    });
+
+    it('should work with one value', function() {
+      match('a{b}c', ['a{b}c']);
+      match('a/b/c{d}e', ['a/b/c{d}e']);
+    });
+
+    it('should work with one value in `bash` mode', function() {
+      match('a{b}c', ['a{b}c']);
+      match('a/b/c{d}e', ['a/b/c{d}e']);
+    });
+
+    it('should work with nested non-sets', function() {
+      match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+    });
+
+    it('should work with nested non-sets in `bash` mode', function() {
+      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+    });
+
+    it('should not expand dots with leading slashes (escaped or paths).', function() {
+      match('a{b,c/*/../d}e', ['abe', 'ac/*/../de']);
+      match('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd']);
+    });
+
+    it('should work with commas.', function() {
+      match('a{b,}c', ['abc', 'ac']);
+      match('a{,b}c', ['ac', 'abc']);
+    });
+
+    it('should expand sets', function() {
+      match('a/{x,y}/cde', ['a/x/cde', 'a/y/cde']);
+      match('a/b/c/{x,y}', ['a/b/c/x', 'a/b/c/y']);
+      match('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
+      match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+      match('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
+      match('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
+    });
+
+    it('should expand multiple sets', function() {
+      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/b/c/e', 'a/a/d/e', 'a/b/d/e']);
+      match('a{b,c}d{e,f}g', ['abdeg', 'acdeg', 'abdfg', 'acdfg']);
+      match('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/y/cdf.md', 'a/x/cef.md', 'a/y/cef.md', 'a/x/cdf.txt', 'a/y/cdf.txt', 'a/x/cef.txt', 'a/y/cef.txt']);
+    });
+
+    it('should expand nested sets', function() {
+      match('a/{b,c,{d,e}}/g', ['a/b/g', 'a/c/g', 'a/d/g', 'a/e/g']);
+      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
+      match('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
+      match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
+      match('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
+      match('a{{x,y},z}b', ['axb', 'azb', 'ayb']);
+      match('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
+      match('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'ahx/z', 'acex/z']);
+      match('a{b,c{d,e},h}x{y,z}', ['abxy', 'acdxy', 'ahxy', 'acexy', 'abxz', 'acdxz', 'ahxz', 'acexz']);
+      match('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'acdxy', 'afhxy', 'acexy', 'aghxy', 'abxz', 'acdxz', 'afhxz', 'acexz', 'aghxz']);
+      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+    });
+
+    it('should expand with globs.', function() {
+      match('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
+      match('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
+      match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/e/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.txt']);
+    });
+
+    it('should expand with extglobs.', function() {
+      match('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
+    });
+  });
+
+  describe('escaping:', function() {
+    it('should not expand strings with es6/bash-like variables.', function() {
+      match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+      match('a${b}c', ['a${b}c']);
+      match('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
+      match('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
+    });
+
+    it('should not expand escaped commas.', function() {
+      match('a{b\\,c}d', ['a{b,c}d']);
+      match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+      match('{abc\\,def}', ['{abc,def}']);
+      match('{abc\\,def,ghi}', ['abc,def', 'ghi']);
+      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+    });
+
+    it('should return sets with escaped commas in `bash` mode.', function() {
+      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+    });
+
+    it('should not expand escaped braces.', function() {
+      match('{a,b\\}c,d}', ['a', 'b}c', 'd']);
+      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      match('a/{b,\\{a,b,c,d,e}/d', ['a/b/d', 'a/b/d', 'a/{a/d', 'a/c/d', 'a/d/d', 'a/e/d']);
+      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
+      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+    });
+
+    it('should not expand escaped braces or commas.', function() {
+      match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+    });
+  });
+
+  describe('complex', function() {
+    it('should expand a complex combination of ranges and sets:', function() {
+      match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', ' [...]
+    });
+
+    it('should expand complex sets and ranges in `bash` mode:', function() {
+      match('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/y/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e']);
+    });
+  });
+});
+
+describe('range expansion', function() {
+  it('should expand numerical ranges', function() {
+    match('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
+    match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+    match('x{3..3}y', ['x3y']);
+    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+    match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{1..3}', ['1', '2', '3']);
+    match('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
+    match('{3..3}', ['3']);
+    match('{5..8}', ['5', '6', '7', '8']);
+  });
+
+  it('should expand alphabetical ranges', function() {
+    match('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
+    match('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
+    match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+    match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+    match('{a..e}', ['a', 'b', 'c', 'd', 'e']);
+    match('{A..E}', ['A', 'B', 'C', 'D', 'E']);
+    match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+    match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+    match('{E..A}', ['E', 'D', 'C', 'B', 'A']);
+    match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+    match('{f..f}', ['f']);
+  });
+
+  it('should use steps with alphabetical ranges', function() {
+    match('{a..e..2}', ['a', 'c', 'e']);
+    match('{E..A..2}', ['E', 'C', 'A']);
+  });
+
+  it('should not try to expand ranges with decimals', function() {
+    match('{1.1..2.1}', ['{1.1..2.1}']);
+    match('{1.1..2.1}', ['{1.1..2.1}'], {optimize: true});
+    match('{1.1..~2.1}', ['{1.1..~2.1}'], {optimize: true});
+  });
+
+  it('should expand negative ranges', function() {
+    match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+    match('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
+    match('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
+    match('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
+  });
+
+  it('should expand multiple ranges:', function() {
+    match('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/c/e/f', 'a/d/e/f', 'a/b/e/g', 'a/c/e/g', 'a/d/e/g', 'a/b/e/h', 'a/c/e/h', 'a/d/e/h']);
+  });
+
+  it('should work with dots in file paths', function() {
+    match('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
+  });
+
+  it('should make a regex-string when `options.optimize` is defined:', function() {
+    match('../{1..3}/../foo', ['../([1-3])/../foo'], {optimize: true});
+    match('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo'], {optimize: true});
+    match('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo'], {optimize: true});
+    match('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+    match('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+  });
+
+  it('should expand ranges using steps:', function() {
+    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+    match('{1..10..2}', ['1', '3', '5', '7', '9']);
+    match('{1..20..20}', ['1']);
+    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+    match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    match('{10..1..2}', ['10', '8', '6', '4', '2']);
+    match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+    match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+    match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+    match('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{1..10..2}', ['1', '3', '5', '7', '9']);
+    match('{1..20..20}', ['1']);
+    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    match('{10..1..2}', ['10', '8', '6', '4', '2']);
+    match('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{2..10..2}', ['2', '4', '6', '8', '10']);
+    match('{2..10..3}', ['2', '5', '8']);
+  });
+
+  it('should expand negative ranges using steps:', function() {
+    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    match('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
+    match('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    match('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
+    match('{-2..-10..3}', ['-2', '-5', '-8']);
+    match('{-9..9..3}', ['-9', '-6', '-3', '0', '3', '6', '9']);
+  });
+
+  it('should expand mixed ranges and sets:', function() {
+    match('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+    match('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+  });
+
+  it('should return invalid ranges:', function() {
+    match('{1.20..2}', ['{1.20..2}']);
+    match('{1..0f}', ['{1..0f}']);
+    match('{1..10..ff}', ['{1..10..ff}']);
+    match('{1..10.f}', ['{1..10.f}']);
+    match('{1..10f}', ['{1..10f}']);
+    match('{1..20..2f}', ['{1..20..2f}']);
+    match('{1..20..f2}', ['{1..20..f2}']);
+    match('{1..2f..2}', ['{1..2f..2}']);
+    match('{1..ff..2}', ['{1..ff..2}']);
+    match('{1..ff}', ['{1..ff}']);
+  });
+});
diff --git a/test/support/bash.js b/test/support/bash.js
new file mode 100644
index 0000000..b06c73e
--- /dev/null
+++ b/test/support/bash.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var isWindows = require('is-windows');
+var spawn = require('cross-spawn');
+var utils = require('./utils');
+
+/**
+ * Expose `bash` util
+ */
+
+module.exports = function(pattern) {
+  if (isWindows()) {
+    throw new Error('windows not supported');
+  }
+
+  var cmd = pattern;
+  if (!/echo/.test(cmd)) {
+    cmd = 'echo ' + escape(pattern);
+  }
+
+  var res = spawn.sync(utils.getBashPath(), ['-c', cmd]);
+  var err = res.stderr && res.stderr.toString().trim();
+  if (err) {
+    console.error(cmd);
+    throw new Error(err);
+  }
+
+  if (!res.stdout) {
+    return [];
+  }
+  return unescape(res.stdout).sort();
+};
+
+/**
+ * Escape characters that behave differently in bash than node (like spaces, which are
+ * valid path characters in node.js but indicate a delimiter in Bash)
+ */
+
+function escape(buf) {
+  return buf.split(/\\? /).join('_SPACE_')
+    .replace(/([*`\[\]])/g, '\\$1')
+    .replace(/(\$\{)([^{}]+)(\})/g, function(m, $1, $2, $3) {
+      return utils.nc[0] + $2 + utils.nc[2];
+    });
+}
+
+/**
+ * Unescape previously-escaped characters
+ */
+
+function unescape(buf) {
+  return buf.toString().split(/[ \n]/)
+    .filter(Boolean)
+    .map(function(str) {
+      return utils.unescape(str, {escape: true})
+        .split('_SPACE_').join(' ')
+        .split(/\\(?!`)/).join('');
+    });
+}
diff --git a/test/support/generate.js b/test/support/generate.js
new file mode 100644
index 0000000..5ab1a77
--- /dev/null
+++ b/test/support/generate.js
@@ -0,0 +1,57 @@
+'use strict';
+
+var braces = require('../..');
+var mm = require('minimatch');
+var size = require('pretty-bytes');
+var text = require('text-table');
+var Time = require('time-diff');
+var time = new Time();
+
+var table = [
+  ['**Pattern**', '**minimatch**', '**braces**'],
+  ['---', '---', '---'],
+];
+
+function generate(pattern) {
+  // time.start('minimatch');
+
+  // var mval = mm.braceExpand(pattern).join('|');
+  // var m = [wrap(size(mval.length)), '(' + time.end('minimatch', 'μs') + ')'].join(' ');
+
+  time.start('braces');
+  var bval = braces.expand(pattern).join('|');
+  var b = [wrap(size(bval.length)), '(' + time.end('braces', 'μs') + ')'].join(' ');
+
+  table.push([wrap(pattern), 'm', b]);
+  return table;
+}
+
+function wrap(str) {
+  return '`' + str + '`';
+}
+
+var patterns = [
+  '{1..9007199254740991}',
+  // 'a/{1..10000000}',
+  // 'a/{1..1000000}',
+  // 'a/{1..100000}',
+  // 'a/{1..10000}',
+  // 'a/{1..1000}',
+  // 'a/{1..100}',
+  // 'a/{1..10}',
+  // 'a/{1..3}',
+  // 'a/{2000..2016}/bar/{a..j}/baz',
+  // 'a/{1900..2016}/bar/{a..j}/baz',
+  // 'a/{1000..2016}/bar/{a..j}/baz',
+  // 'a/{100..2016}/bar/{a..j}/baz',
+];
+
+var len = patterns.length;
+var idx = -1;
+var res = [];
+
+while (++idx < len) {
+  generate(patterns[idx]);
+}
+
+console.log(text(table, {hsep: ' | '}));
diff --git a/test/support/utils.js b/test/support/utils.js
new file mode 100644
index 0000000..6e214ad
--- /dev/null
+++ b/test/support/utils.js
@@ -0,0 +1,59 @@
+'use strict';
+
+var util = require('util');
+var bashPath = '';
+
+/**
+ * Utils
+ */
+
+exports.exists = require('fs-exists-sync');
+exports.extend = require('extend-shallow');
+exports.nc = require('noncharacters');
+
+exports.getBashPath = function() {
+  if (bashPath) return bashPath;
+  if (exports.exists('/usr/local/bin/bash')) {
+    bashPath = '/usr/local/bin/bash';
+  } else if (exports.exists('/bin/bash')) {
+    bashPath = '/bin/bash';
+  } else {
+    bashPath = 'bash';
+  }
+  return bashPath;
+};
+
+exports.escape = function(str, options) {
+  if (typeof str !== 'string') {
+    throw new TypeError('expected a string: ' + util.inspect(str));
+  }
+  var opts = exports.extend({}, options);
+  if (!opts.expand && !opts.escape) return str;
+  str = str.replace(/(\$\{([^{}]+?)\})/g, function(m, $1, $2) {
+    return exports.nc[0] + $2 + exports.nc[2];
+  });
+  str = str.replace(/(\{)([^{,.}]+?)(\})/g, function(m, $1, $2, $3) {
+    return exports.nc[1] + $2 + exports.nc[2];
+  });
+  str = str.replace(/\\\{|\{(?!.*\})/g, exports.nc[1]);
+  str = str.replace(/\\\}/g, exports.nc[2]);
+  str = str.replace(/\\\,/g, exports.nc[3]);
+  if (!/\{/.test(str)) {
+    return str.replace(/\}/g, exports.nc[2]);
+  }
+  return str;
+};
+
+exports.unescape = function(str, options) {
+  if (typeof str !== 'string') {
+    throw new TypeError('expected a string: ' + util.inspect(str));
+  }
+  var opts = exports.extend({}, options);
+  if (!opts.expand && !opts.escape) return str;
+  var pre = opts.noescape ? '' : '\\';
+  str = str.split(exports.nc[0]).join(pre ? '\\$\\{' : '${');
+  str = str.split(exports.nc[1]).join(pre + '{');
+  str = str.split(exports.nc[2]).join(pre + '}');
+  str = str.split(exports.nc[3]).join(',');
+  return str.replace(/\\+/g, '\\');
+};

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



More information about the Pkg-javascript-commits mailing list