[Pkg-javascript-commits] [node-parse-glob] 02/08: Import Upstream version 3.0.4

Sruthi Chandran srud-guest at moszumanska.debian.org
Tue Nov 1 10:31:49 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-parse-glob.

commit a0910df135b68efac3915282d04746479f047d47
Author: Sruthi <srud at disroot.org>
Date:   Tue Nov 1 15:17:21 2016 +0530

    Import Upstream version 3.0.4
---
 .editorconfig  |   27 ++
 .gitattributes |   10 +
 .gitignore     |   15 +
 .jshintrc      |   18 +
 .travis.yml    |    8 +
 .verb.md       |   96 ++++++
 LICENSE        |   21 ++
 README.md      |  115 +++++++
 browser.js     |  554 ++++++++++++++++++++++++++++++
 index.js       |  156 +++++++++
 package.json   |   62 ++++
 test.js        | 1045 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 2127 insertions(+)

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..32dd133
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,27 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+end_of_line = lf
+charset = utf-8
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.json]
+indent_style = space
+indent_size = 2
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.md]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = false
+
+[test/fixtures/*]
+trim_trailing_whitespace = false
+insert_final_newline = false
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..4a3f1d3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# Enforce Unix newlines
+* text eol=lf
+
+# binaries
+*.ai binary
+*.psd binary
+*.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..c22270c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.DS_Store
+*.DS_store
+*.sublime-*
+_gh_pages
+bower_components
+node_modules
+npm-debug.log
+actual
+test/actual
+temp
+tmp
+TODO.md
+vendor
+support.js
+example.js
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..6e5a84a
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,18 @@
+{
+  "asi": false,
+  "boss": true,
+  "curly": true,
+  "eqeqeq": true,
+  "eqnull": true,
+  "esnext": true,
+  "immed": true,
+  "latedef": false,
+  "laxcomma": false,
+  "mocha": true,
+  "newcap": true,
+  "noarg": true,
+  "node": true,
+  "sub": true,
+  "undef": true,
+  "unused": true
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..efe94c0
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+sudo: false
+language: node_js
+node_js:
+  - "0.10"
+  - "0.12"
+  - "iojs"
+git:
+  depth: 10
\ No newline at end of file
diff --git a/.verb.md b/.verb.md
new file mode 100644
index 0000000..d16000e
--- /dev/null
+++ b/.verb.md
@@ -0,0 +1,96 @@
+# {%= name %} {%= badge("fury") %} {%= badge("travis") %}
+
+> {%= description %}
+
+**Changes from v1.0.0 to v{%= version %}** 
+
+- all path-related properties are now on the `path` object
+- all boolean properties are now on the `is` object
+- adds `base` property
+
+See the [properties](#properties) section for details.
+
+{%= include("install-npm", {save: true}) %}
+
+- parses 1,000+ glob patterns in 29ms (2.3 GHz Intel Core i7)
+- Extensive [unit tests](./test.js) (more than 1,000 lines), covering wildcards, globstars, character classes, brace patterns, extglobs, dotfiles and other complex patterns.
+
+See the tests for [hundreds of examples](./test.js).
+
+## Usage
+
+```js
+var parseGlob = require('{%= name %}');
+```
+
+**Example**
+
+```js
+parseGlob('a/b/c/**/*.{yml,json}');
+```
+
+**Returns:**
+
+```js
+{ orig: 'a/b/c/**/*.{yml,json}',
+  is:
+   { glob: true,
+     negated: false,
+     extglob: false,
+     braces: true,
+     brackets: false,
+     globstar: true,
+     dotfile: false,
+     dotdir: false },
+  glob: '**/*.{yml,json}',
+  base: 'a/b/c',
+  path:
+   { dirname: 'a/b/c/**/',
+     basename: '*.{yml,json}',
+     filename: '*',
+     extname: '.{yml,json}',
+     ext: '{yml,json}' } }
+```
+
+## Properties
+
+The object returned by parseGlob has the following properties:
+
+- `orig`: a copy of the original, unmodified glob pattern
+- `is`: an object with boolean information about the glob:
+  + `glob`: true if the pattern actually a glob pattern
+  + `negated`: true if it's a negation pattern (`!**/foo.js`)
+  + `extglob`: true if it has extglobs (`@(foo|bar)`)
+  + `braces`: true if it has braces (`{1..2}` or `.{txt,md}`)
+  + `brackets`: true if it has POSIX brackets (`[[:alpha:]]`)
+  + `globstar`: true if the pattern has a globstar (double star, `**`)
+  + `dotfile`: true if the pattern should match dotfiles 
+  + `dotdir`: true if the pattern should match dot-directories (like `.git`)
+- `glob`: the glob pattern part of the string, if any
+- `base`: the non-glob part of the string, if any
+- `path`: file path segments
+  + `dirname`: directory
+  + `basename`: file name with extension
+  + `filename`: file name without extension
+  + `extname`: file extension with dot
+  + `ext`: file extension without dot
+
+## Related
+{%= related(['glob-base', 'glob-parent', 'is-glob', 'glob-path-regex', 'micromatch']) %}
+
+## Contributing
+{%= include("contributing") %}
+
+## Tests
+{%= include("tests") %}
+
+## Author
+{%= include("author") %}
+
+## License
+{%= copyright({year: 2014}) %}
+{%= license() %}
+
+***
+
+{%= include("footer") %}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65f90ac
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015, 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..000ccd9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,115 @@
+# parse-glob [![NPM version](https://badge.fury.io/js/parse-glob.svg)](http://badge.fury.io/js/parse-glob)  [![Build Status](https://travis-ci.org/jonschlinkert/parse-glob.svg)](https://travis-ci.org/jonschlinkert/parse-glob)
+
+> Parse a glob pattern into an object of tokens.
+
+**Changes from v1.0.0 to v3.0.4**
+
+* all path-related properties are now on the `path` object
+* all boolean properties are now on the `is` object
+* adds `base` property
+
+See the [properties](#properties) section for details.
+
+Install with [npm](https://www.npmjs.com/)
+
+```sh
+$ npm i parse-glob --save
+```
+
+* parses 1,000+ glob patterns in 29ms (2.3 GHz Intel Core i7)
+* Extensive [unit tests](./test.js) (more than 1,000 lines), covering wildcards, globstars, character classes, brace patterns, extglobs, dotfiles and other complex patterns.
+
+See the tests for [hundreds of examples](./test.js).
+
+## Usage
+
+```js
+var parseGlob = require('parse-glob');
+```
+
+**Example**
+
+```js
+parseGlob('a/b/c/**/*.{yml,json}');
+```
+
+**Returns:**
+
+```js
+{ orig: 'a/b/c/**/*.{yml,json}',
+  is:
+   { glob: true,
+     negated: false,
+     extglob: false,
+     braces: true,
+     brackets: false,
+     globstar: true,
+     dotfile: false,
+     dotdir: false },
+  glob: '**/*.{yml,json}',
+  base: 'a/b/c',
+  path:
+   { dirname: 'a/b/c/**/',
+     basename: '*.{yml,json}',
+     filename: '*',
+     extname: '.{yml,json}',
+     ext: '{yml,json}' } }
+```
+
+## Properties
+
+The object returned by parseGlob has the following properties:
+
+* `orig`: a copy of the original, unmodified glob pattern
+* `is`: an object with boolean information about the glob:
+  - `glob`: true if the pattern actually a glob pattern
+  - `negated`: true if it's a negation pattern (`!**/foo.js`)
+  - `extglob`: true if it has extglobs (`@(foo|bar)`)
+  - `braces`: true if it has braces (`{1..2}` or `.{txt,md}`)
+  - `brackets`: true if it has POSIX brackets (`[[:alpha:]]`)
+  - `globstar`: true if the pattern has a globstar (double star, `**`)
+  - `dotfile`: true if the pattern should match dotfiles
+  - `dotdir`: true if the pattern should match dot-directories (like `.git`)
+* `glob`: the glob pattern part of the string, if any
+* `base`: the non-glob part of the string, if any
+* `path`: file path segments
+  - `dirname`: directory
+  - `basename`: file name with extension
+  - `filename`: file name without extension
+  - `extname`: file extension with dot
+  - `ext`: file extension without dot
+
+## Related
+* [glob-base](https://www.npmjs.com/package/glob-base): Returns an object with the (non-glob) base path and the actual pattern. | [homepage](https://github.com/jonschlinkert/glob-base)
+* [glob-parent](https://www.npmjs.com/package/glob-parent): Strips glob magic from a string to provide the parent path | [homepage](https://github.com/es128/glob-parent)
+* [glob-path-regex](https://www.npmjs.com/package/glob-path-regex): Regular expression for matching the parts of glob pattern. | [homepage](https://github.com/regexps/glob-path-regex)
+* [is-glob](https://www.npmjs.com/package/is-glob): Returns `true` if the given string looks like a glob pattern. | [homepage](https://github.com/jonschlinkert/is-glob)
+* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. Just… [more](https://www.npmjs.com/package/micromatch) | [homepage](https://github.com/jonschlinkert/micromatch)
+
+## Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/parse-glob/issues/new).
+
+## Tests
+
+Install dev dependencies:
+
+```sh
+$ npm i -d && npm test
+```
+
+## Author
+
+**Jon Schlinkert**
+
++ [github/jonschlinkert](https://github.com/jonschlinkert)
++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+## License
+
+Copyright © 2014-2015 Jon Schlinkert
+Released under the MIT license.
+
+***
+
+_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on September 22, 2015._
\ No newline at end of file
diff --git a/browser.js b/browser.js
new file mode 100644
index 0000000..c1d411f
--- /dev/null
+++ b/browser.js
@@ -0,0 +1,554 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/*!
+ * parse-glob <https://github.com/jonschlinkert/parse-glob>
+ *
+ * Copyright (c) 2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+var isGlob = require('is-glob');
+var findBase = require('glob-base');
+var extglob = require('is-extglob');
+var dotfile = require('is-dotfile');
+
+/**
+ * Expose `cache`
+ */
+
+var cache = module.exports.cache = {};
+
+/**
+ * Parse a glob pattern into tokens.
+ *
+ * When no paths or '**' are in the glob, we use a
+ * different strategy for parsing the filename, since
+ * file names can contain braces and other difficult
+ * patterns. such as:
+ *
+ *  - `*.{a,b}`
+ *  - `(**|*.js)`
+ */
+
+module.exports = function parseGlob(glob) {
+  if (cache.hasOwnProperty(glob)) {
+    return cache[glob];
+  }
+
+  var tok = {};
+  tok.orig = glob;
+  tok.is = {};
+
+  // unescape dots and slashes in braces/brackets
+  glob = escape(glob);
+
+  var parsed = findBase(glob);
+  tok.is.glob = parsed.isGlob;
+
+  tok.glob = parsed.glob;
+  tok.base = parsed.base;
+  var segs = /([^\/]*)$/.exec(glob);
+
+  tok.path = {};
+  tok.path.dirname = '';
+  tok.path.basename = segs[1] || '';
+  tok.path.dirname = glob.split(tok.path.basename).join('') || '';
+  var basename = (tok.path.basename || '').split('.') || '';
+  tok.path.filename = basename[0] || '';
+  tok.path.extname = basename.slice(1).join('.') || '';
+  tok.path.ext = '';
+
+  if (isGlob(tok.path.dirname) && !tok.path.basename) {
+    if (!/\/$/.test(tok.glob)) {
+      tok.path.basename = tok.glob;
+    }
+    tok.path.dirname = tok.base;
+  }
+
+  if (glob.indexOf('/') === -1 && !tok.is.globstar) {
+    tok.path.dirname = '';
+    tok.path.basename = tok.orig;
+  }
+
+  var dot = tok.path.basename.indexOf('.');
+  if (dot !== -1) {
+    tok.path.filename = tok.path.basename.slice(0, dot);
+    tok.path.extname = tok.path.basename.slice(dot);
+  }
+
+  if (tok.path.extname.charAt(0) === '.') {
+    var exts = tok.path.extname.split('.');
+    tok.path.ext = exts[exts.length - 1];
+  }
+
+  // unescape dots and slashes in braces/brackets
+  tok.glob = unescape(tok.glob);
+  tok.path.dirname = unescape(tok.path.dirname);
+  tok.path.basename = unescape(tok.path.basename);
+  tok.path.filename = unescape(tok.path.filename);
+  tok.path.extname = unescape(tok.path.extname);
+
+  // Booleans
+  var is = (glob && tok.is.glob);
+  tok.is.negated  = glob && glob.charAt(0) === '!';
+  tok.is.extglob  = glob && extglob(glob);
+  tok.is.braces   = has(is, glob, '{');
+  tok.is.brackets = has(is, glob, '[:');
+  tok.is.globstar = has(is, glob, '**');
+  tok.is.dotfile  = dotfile(tok.path.basename) || dotfile(tok.path.filename);
+  tok.is.dotdir   = dotdir(tok.path.dirname);
+  return (cache[glob] = tok);
+}
+
+/**
+ * Returns true if the glob matches dot-directories.
+ *
+ * @param  {Object} `tok` The tokens object
+ * @param  {Object} `path` The path object
+ * @return {Object}
+ */
+
+function dotdir(base) {
+  if (base.indexOf('/.') !== -1) {
+    return true;
+  }
+  if (base.charAt(0) === '.' && base.charAt(1) !== '/') {
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Returns true if the pattern has the given `ch`aracter(s)
+ *
+ * @param  {Object} `glob` The glob pattern.
+ * @param  {Object} `ch` The character to test for
+ * @return {Object}
+ */
+
+function has(is, glob, ch) {
+  return is && glob.indexOf(ch) !== -1;
+}
+
+/**
+ * Escape/unescape utils
+ */
+
+function escape(str) {
+  var re = /\{([^{}]*?)}|\(([^()]*?)\)|\[([^\[\]]*?)\]/g;
+  return str.replace(re, function (outter, braces, parens, brackets) {
+    var inner = braces || parens || brackets;
+    if (!inner) { return outter; }
+    return outter.split(inner).join(esc(inner));
+  });
+}
+
+function esc(str) {
+  str = str.split('/').join('__SLASH__');
+  str = str.split('.').join('__DOT__');
+  return str;
+}
+
+function unescape(str) {
+  str = str.split('__SLASH__').join('/');
+  str = str.split('__DOT__').join('.');
+  return str;
+}
+
+},{"glob-base":4,"is-dotfile":6,"is-extglob":7,"is-glob":8}],2:[function(require,module,exports){
+(function (process){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// 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.
+
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+  // if the path tries to go above the root, `up` ends up > 0
+  var up = 0;
+  for (var i = parts.length - 1; i >= 0; i--) {
+    var last = parts[i];
+    if (last === '.') {
+      parts.splice(i, 1);
+    } else if (last === '..') {
+      parts.splice(i, 1);
+      up++;
+    } else if (up) {
+      parts.splice(i, 1);
+      up--;
+    }
+  }
+
+  // if the path is allowed to go above the root, restore leading ..s
+  if (allowAboveRoot) {
+    for (; up--; up) {
+      parts.unshift('..');
+    }
+  }
+
+  return parts;
+}
+
+// Split a filename into [root, dir, basename, ext], unix version
+// 'root' is just a slash, or nothing.
+var splitPathRe =
+    /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
+var splitPath = function(filename) {
+  return splitPathRe.exec(filename).slice(1);
+};
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+  var resolvedPath = '',
+      resolvedAbsolute = false;
+
+  for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
+    var path = (i >= 0) ? arguments[i] : process.cwd();
+
+    // Skip empty and invalid entries
+    if (typeof path !== 'string') {
+      throw new TypeError('Arguments to path.resolve must be strings');
+    } else if (!path) {
+      continue;
+    }
+
+    resolvedPath = path + '/' + resolvedPath;
+    resolvedAbsolute = path.charAt(0) === '/';
+  }
+
+  // At this point the path should be resolved to a full absolute path, but
+  // handle relative paths to be safe (might happen when process.cwd() fails)
+
+  // Normalize the path
+  resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+    return !!p;
+  }), !resolvedAbsolute).join('/');
+
+  return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+  var isAbsolute = exports.isAbsolute(path),
+      trailingSlash = substr(path, -1) === '/';
+
+  // Normalize the path
+  path = normalizeArray(filter(path.split('/'), function(p) {
+    return !!p;
+  }), !isAbsolute).join('/');
+
+  if (!path && !isAbsolute) {
+    path = '.';
+  }
+  if (path && trailingSlash) {
+    path += '/';
+  }
+
+  return (isAbsolute ? '/' : '') + path;
+};
+
+// posix version
+exports.isAbsolute = function(path) {
+  return path.charAt(0) === '/';
+};
+
+// posix version
+exports.join = function() {
+  var paths = Array.prototype.slice.call(arguments, 0);
+  return exports.normalize(filter(paths, function(p, index) {
+    if (typeof p !== 'string') {
+      throw new TypeError('Arguments to path.join must be strings');
+    }
+    return p;
+  }).join('/'));
+};
+
+
+// path.relative(from, to)
+// posix version
+exports.relative = function(from, to) {
+  from = exports.resolve(from).substr(1);
+  to = exports.resolve(to).substr(1);
+
+  function trim(arr) {
+    var start = 0;
+    for (; start < arr.length; start++) {
+      if (arr[start] !== '') break;
+    }
+
+    var end = arr.length - 1;
+    for (; end >= 0; end--) {
+      if (arr[end] !== '') break;
+    }
+
+    if (start > end) return [];
+    return arr.slice(start, end - start + 1);
+  }
+
+  var fromParts = trim(from.split('/'));
+  var toParts = trim(to.split('/'));
+
+  var length = Math.min(fromParts.length, toParts.length);
+  var samePartsLength = length;
+  for (var i = 0; i < length; i++) {
+    if (fromParts[i] !== toParts[i]) {
+      samePartsLength = i;
+      break;
+    }
+  }
+
+  var outputParts = [];
+  for (var i = samePartsLength; i < fromParts.length; i++) {
+    outputParts.push('..');
+  }
+
+  outputParts = outputParts.concat(toParts.slice(samePartsLength));
+
+  return outputParts.join('/');
+};
+
+exports.sep = '/';
+exports.delimiter = ':';
+
+exports.dirname = function(path) {
+  var result = splitPath(path),
+      root = result[0],
+      dir = result[1];
+
+  if (!root && !dir) {
+    // No dirname whatsoever
+    return '.';
+  }
+
+  if (dir) {
+    // It has a dirname, strip trailing slash
+    dir = dir.substr(0, dir.length - 1);
+  }
+
+  return root + dir;
+};
+
+
+exports.basename = function(path, ext) {
+  var f = splitPath(path)[2];
+  // TODO: make this comparison case-insensitive on windows?
+  if (ext && f.substr(-1 * ext.length) === ext) {
+    f = f.substr(0, f.length - ext.length);
+  }
+  return f;
+};
+
+
+exports.extname = function(path) {
+  return splitPath(path)[3];
+};
+
+function filter (xs, f) {
+    if (xs.filter) return xs.filter(f);
+    var res = [];
+    for (var i = 0; i < xs.length; i++) {
+        if (f(xs[i], i, xs)) res.push(xs[i]);
+    }
+    return res;
+}
+
+// String.prototype.substr - negative index don't work in IE8
+var substr = 'ab'.substr(-1) === 'b'
+    ? function (str, start, len) { return str.substr(start, len) }
+    : function (str, start, len) {
+        if (start < 0) start = str.length + start;
+        return str.substr(start, len);
+    }
+;
+
+}).call(this,require('_process'))
+},{"_process":3}],3:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+
+function drainQueue() {
+    if (draining) {
+        return;
+    }
+    draining = true;
+    var currentQueue;
+    var len = queue.length;
+    while(len) {
+        currentQueue = queue;
+        queue = [];
+        var i = -1;
+        while (++i < len) {
+            currentQueue[i]();
+        }
+        len = queue.length;
+    }
+    draining = false;
+}
+process.nextTick = function (fun) {
+    queue.push(fun);
+    if (!draining) {
+        setTimeout(drainQueue, 0);
+    }
+};
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+    throw new Error('process.binding is not supported');
+};
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+    throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],4:[function(require,module,exports){
+/*!
+ * glob-base <https://github.com/jonschlinkert/glob-base>
+ *
+ * Copyright (c) 2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+var path = require('path');
+var parent = require('glob-parent');
+var isGlob = require('is-glob');
+
+module.exports = function globBase(pattern) {
+  if (typeof pattern !== 'string') {
+    throw new TypeError('glob-base expects a string.');
+  }
+
+  var res = {};
+  res.base = parent(pattern);
+  res.isGlob = isGlob(pattern);
+
+  if (res.base !== '.') {
+    res.glob = pattern.substr(res.base.length);
+    if (res.glob.charAt(0) === '/') {
+      res.glob = res.glob.substr(1);
+    }
+  } else {
+    res.glob = pattern;
+  }
+
+  if (!res.isGlob) {
+    res.base = dirname(pattern);
+    res.glob = res.base !== '.'
+      ? pattern.substr(res.base.length)
+      : pattern;
+  }
+
+  if (res.glob.substr(0, 2) === './') {
+    res.glob = res.glob.substr(2);
+  }
+  if (res.glob.charAt(0) === '/') {
+    res.glob = res.glob.substr(1);
+  }
+  return res;
+};
+
+function dirname(glob) {
+  if (glob.slice(-1) === '/') return glob;
+  return path.dirname(glob);
+}
+
+},{"glob-parent":5,"is-glob":8,"path":2}],5:[function(require,module,exports){
+'use strict';
+
+var path = require('path');
+var isglob = require('is-glob');
+
+module.exports = function globParent(str) {
+	str += 'a'; // preserves full path in case of trailing path separator
+	do {str = path.dirname(str)} while (isglob(str));
+	return str;
+};
+
+},{"is-glob":8,"path":2}],6:[function(require,module,exports){
+/*!
+ * is-dotfile <https://github.com/regexps/is-dotfile>
+ *
+ * Copyright (c) 2015 Jon Schlinkert, contributors.
+ * Licensed under the MIT license.
+ */
+
+module.exports = function(str) {
+  if (str.charCodeAt(0) === 46 /* . */ && str.indexOf('/', 1) === -1) {
+    return true;
+  }
+
+  var last = str.lastIndexOf('/');
+  return last !== -1 ? str.charCodeAt(last + 1) === 46  /* . */ : false;
+};
+
+},{}],7:[function(require,module,exports){
+/*!
+ * is-extglob <https://github.com/jonschlinkert/is-extglob>
+ *
+ * Copyright (c) 2014-2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+module.exports = function isExtglob(str) {
+  return typeof str === 'string'
+    && /[@?!+*]\(/.test(str);
+};
+
+},{}],8:[function(require,module,exports){
+/*!
+ * is-glob <https://github.com/jonschlinkert/is-glob>
+ *
+ * Copyright (c) 2014-2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+module.exports = function isGlob(str) {
+  return typeof str === 'string'
+    && /[@!*+{}?(|)[\]]/.test(str);
+};
+},{}]},{},[1]);
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..4ab691a
--- /dev/null
+++ b/index.js
@@ -0,0 +1,156 @@
+/*!
+ * parse-glob <https://github.com/jonschlinkert/parse-glob>
+ *
+ * Copyright (c) 2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+var isGlob = require('is-glob');
+var findBase = require('glob-base');
+var extglob = require('is-extglob');
+var dotfile = require('is-dotfile');
+
+/**
+ * Expose `cache`
+ */
+
+var cache = module.exports.cache = {};
+
+/**
+ * Parse a glob pattern into tokens.
+ *
+ * When no paths or '**' are in the glob, we use a
+ * different strategy for parsing the filename, since
+ * file names can contain braces and other difficult
+ * patterns. such as:
+ *
+ *  - `*.{a,b}`
+ *  - `(**|*.js)`
+ */
+
+module.exports = function parseGlob(glob) {
+  if (cache.hasOwnProperty(glob)) {
+    return cache[glob];
+  }
+
+  var tok = {};
+  tok.orig = glob;
+  tok.is = {};
+
+  // unescape dots and slashes in braces/brackets
+  glob = escape(glob);
+
+  var parsed = findBase(glob);
+  tok.is.glob = parsed.isGlob;
+
+  tok.glob = parsed.glob;
+  tok.base = parsed.base;
+  var segs = /([^\/]*)$/.exec(glob);
+
+  tok.path = {};
+  tok.path.dirname = '';
+  tok.path.basename = segs[1] || '';
+  tok.path.dirname = glob.split(tok.path.basename).join('') || '';
+  var basename = (tok.path.basename || '').split('.') || '';
+  tok.path.filename = basename[0] || '';
+  tok.path.extname = basename.slice(1).join('.') || '';
+  tok.path.ext = '';
+
+  if (isGlob(tok.path.dirname) && !tok.path.basename) {
+    if (!/\/$/.test(tok.glob)) {
+      tok.path.basename = tok.glob;
+    }
+    tok.path.dirname = tok.base;
+  }
+
+  if (glob.indexOf('/') === -1 && !tok.is.globstar) {
+    tok.path.dirname = '';
+    tok.path.basename = tok.orig;
+  }
+
+  var dot = tok.path.basename.indexOf('.');
+  if (dot !== -1) {
+    tok.path.filename = tok.path.basename.slice(0, dot);
+    tok.path.extname = tok.path.basename.slice(dot);
+  }
+
+  if (tok.path.extname.charAt(0) === '.') {
+    var exts = tok.path.extname.split('.');
+    tok.path.ext = exts[exts.length - 1];
+  }
+
+  // unescape dots and slashes in braces/brackets
+  tok.glob = unescape(tok.glob);
+  tok.path.dirname = unescape(tok.path.dirname);
+  tok.path.basename = unescape(tok.path.basename);
+  tok.path.filename = unescape(tok.path.filename);
+  tok.path.extname = unescape(tok.path.extname);
+
+  // Booleans
+  var is = (glob && tok.is.glob);
+  tok.is.negated  = glob && glob.charAt(0) === '!';
+  tok.is.extglob  = glob && extglob(glob);
+  tok.is.braces   = has(is, glob, '{');
+  tok.is.brackets = has(is, glob, '[:');
+  tok.is.globstar = has(is, glob, '**');
+  tok.is.dotfile  = dotfile(tok.path.basename) || dotfile(tok.path.filename);
+  tok.is.dotdir   = dotdir(tok.path.dirname);
+  return (cache[glob] = tok);
+}
+
+/**
+ * Returns true if the glob matches dot-directories.
+ *
+ * @param  {Object} `tok` The tokens object
+ * @param  {Object} `path` The path object
+ * @return {Object}
+ */
+
+function dotdir(base) {
+  if (base.indexOf('/.') !== -1) {
+    return true;
+  }
+  if (base.charAt(0) === '.' && base.charAt(1) !== '/') {
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Returns true if the pattern has the given `ch`aracter(s)
+ *
+ * @param  {Object} `glob` The glob pattern.
+ * @param  {Object} `ch` The character to test for
+ * @return {Object}
+ */
+
+function has(is, glob, ch) {
+  return is && glob.indexOf(ch) !== -1;
+}
+
+/**
+ * Escape/unescape utils
+ */
+
+function escape(str) {
+  var re = /\{([^{}]*?)}|\(([^()]*?)\)|\[([^\[\]]*?)\]/g;
+  return str.replace(re, function (outter, braces, parens, brackets) {
+    var inner = braces || parens || brackets;
+    if (!inner) { return outter; }
+    return outter.split(inner).join(esc(inner));
+  });
+}
+
+function esc(str) {
+  str = str.split('/').join('__SLASH__');
+  str = str.split('.').join('__DOT__');
+  return str;
+}
+
+function unescape(str) {
+  str = str.split('__SLASH__').join('/');
+  str = str.split('__DOT__').join('.');
+  return str;
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a4acb52
--- /dev/null
+++ b/package.json
@@ -0,0 +1,62 @@
+{
+  "name": "parse-glob",
+  "description": "Parse a glob pattern into an object of tokens.",
+  "version": "3.0.4",
+  "homepage": "https://github.com/jonschlinkert/parse-glob",
+  "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
+  "repository": "jonschlinkert/parse-glob",
+  "bugs": {
+    "url": "https://github.com/jonschlinkert/parse-glob/issues"
+  },
+  "license": "MIT",
+  "files": [
+    "index.js"
+  ],
+  "main": "index.js",
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "mocha",
+    "prepublish": "browserify -o browser.js -e index.js"
+  },
+  "dependencies": {
+    "glob-base": "^0.3.0",
+    "is-dotfile": "^1.0.0",
+    "is-extglob": "^1.0.0",
+    "is-glob": "^2.0.0"
+  },
+  "devDependencies": {
+    "browserify": "^9.0.3",
+    "lodash": "^3.3.1",
+    "mocha": "*"
+  },
+  "keywords": [
+    "glob",
+    "match",
+    "bash",
+    "expand",
+    "expansion",
+    "expression",
+    "file",
+    "files",
+    "filter",
+    "find",
+    "glob",
+    "globbing",
+    "globs",
+    "globstar",
+    "match",
+    "matcher",
+    "matches",
+    "matching",
+    "path",
+    "pattern",
+    "patterns",
+    "regex",
+    "regexp",
+    "regular",
+    "shell",
+    "wildcard"
+  ]
+}
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..f512768
--- /dev/null
+++ b/test.js
@@ -0,0 +1,1045 @@
+/*!
+ * parse-glob <https://github.com/jonschlinkert/parse-glob>
+ *
+ * Copyright (c) 2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+var assert = require('assert');
+var parse = require('./');
+
+describe('`is` object:', function () {
+  it('should detect when special characters are in a pattern:', function () {
+    assert.equal(parse('!**{}[:[@()').is.negated, true);
+    assert.equal(parse('!**{}[:[@()').is.glob, true);
+    assert.equal(parse('!**{}[:[@()').is.extglob, true);
+    assert.equal(parse('!**{}[:[@()').is.brackets, true);
+    assert.equal(parse('!**{}[:[@()').is.braces, true);
+  });
+
+  it('should detect when the pattern is a glob pattern:', function () {
+    assert.equal(parse('a.min.js').is.glob, false);
+    assert.equal(parse('*.min.js').is.glob, true);
+    assert.equal(parse('foo/{a,b}.min.js').is.glob, true);
+    assert.equal(parse('foo/(a|b).min.js').is.glob, true);
+    assert.equal(parse('foo/[a-b].min.js').is.glob, true);
+    assert.equal(parse('!foo').is.glob, true);
+  });
+
+  it('should detect when a pattern is negated:', function () {
+    assert.equal(parse('a.min.js').is.negated, false);
+    assert.equal(parse('!*.min.js').is.negated, true);
+    assert.equal(parse('!foo/{a,b}.min.js').is.negated, true);
+    assert.equal(parse('!foo/(a|b).min.js').is.negated, true);
+    assert.equal(parse('!foo/[a-b].min.js').is.negated, true);
+    assert.equal(parse('foo').is.negated, false);
+  });
+
+  it('should detect when a pattern has a globstar:', function () {
+    assert.equal(parse('**').is.globstar, true);
+    assert.equal(parse('**/*.min.js').is.globstar, true);
+    assert.equal(parse('**/*foo.js').is.globstar, true);
+    assert.equal(parse('*/*').is.globstar, false);
+    assert.equal(parse('*/*/**/a.js').is.globstar, true);
+    assert.equal(parse('a.min.js').is.globstar, false);
+    assert.equal(parse('!*.min.js').is.globstar, false);
+    assert.equal(parse('!foo/{a,b}.min.js').is.globstar, false);
+    assert.equal(parse('!foo/(a|b).min.js').is.globstar, false);
+    assert.equal(parse('!foo/[a-b].min.js').is.globstar, false);
+    assert.equal(parse('foo').is.globstar, false);
+  });
+
+  it('should detect when a pattern has brace patterns:', function () {
+    assert.equal(parse('a/b/c').is.braces, false);
+    assert.equal(parse('**/*.{js,min.js}').is.braces, true);
+    assert.equal(parse('**/*{bar,foo}.js').is.braces, true);
+  });
+});
+
+describe('should get a base path:', function () {
+  it('should extract a base path from a glob pattern:', function () {
+    assert.equal(parse('.*').base, '.');
+    assert.equal(parse('.*').glob, '.*');
+
+    assert.equal(parse('./*').base, '.');
+    assert.equal(parse('./*').glob, '*');
+
+    assert.equal(parse('*').base, '.');
+    assert.equal(parse('*').glob, '*');
+
+    assert.equal(parse('**').base, '.');
+    assert.equal(parse('**').glob, '**');
+
+    assert.equal(parse('**/*.md').base, '.');
+    assert.equal(parse('**/*.md').glob, '**/*.md');
+
+    assert.equal(parse('**/*.min.js').base, '.');
+    assert.equal(parse('**/*.min.js').glob, '**/*.min.js');
+
+    assert.equal(parse('**/*foo.js').base, '.');
+    assert.equal(parse('**/*foo.js').glob, '**/*foo.js');
+
+    assert.equal(parse('**/.*').base, '.');
+    assert.equal(parse('**/.*').glob, '**/.*');
+
+    assert.equal(parse('**/d').base, '.');
+    assert.equal(parse('**/d').glob, '**/d');
+
+    assert.equal(parse('*.*').base, '.');
+    assert.equal(parse('*.*').glob, '*.*');
+
+    assert.equal(parse('*.js').base, '.');
+    assert.equal(parse('*.js').glob, '*.js');
+
+    assert.equal(parse('*.md').base, '.');
+    assert.equal(parse('*.md').glob, '*.md');
+
+    assert.equal(parse('*.min.js').base, '.');
+    assert.equal(parse('*.min.js').glob, '*.min.js');
+
+    assert.equal(parse('*/*').base, '.');
+    assert.equal(parse('*/*').glob, '*/*');
+
+    assert.equal(parse('*/*/*/*').base, '.');
+    assert.equal(parse('*/*/*/*').glob, '*/*/*/*');
+
+    assert.equal(parse('*/*/*/e').base, '.');
+    assert.equal(parse('*/*/*/e').glob, '*/*/*/e');
+
+    assert.equal(parse('*/b/*/e').base, '.');
+    assert.equal(parse('*/b/*/e').glob, '*/b/*/e');
+
+    assert.equal(parse('*b').base, '.');
+    assert.equal(parse('*b').glob, '*b');
+
+    assert.equal(parse('./a/**/j/**/z/*.md').base, './a');
+    assert.equal(parse('./a/**/j/**/z/*.md').glob, '**/j/**/z/*.md');
+
+    assert.equal(parse('./a/**/z/*.md').base, './a');
+    assert.equal(parse('./a/**/z/*.md').glob, '**/z/*.md');
+
+    assert.equal(parse('./{a/b/{c,/foo.js}/e.f.g}').base, '.');
+    assert.equal(parse('./{a/b/{c,/foo.js}/e.f.g}').glob, '{a/b/{c,/foo.js}/e.f.g}');
+
+    assert.equal(parse('./node_modules/*-glob/**/*.js').base, './node_modules');
+    assert.equal(parse('./node_modules/*-glob/**/*.js').glob, '*-glob/**/*.js');
+
+    assert.equal(parse('a/b/{c,/.gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,/.gitignore}').glob, '{c,/.gitignore}');
+
+    assert.equal(parse('a/b/.{foo,bar}').base, 'a/b');
+    assert.equal(parse('a/b/.{foo,bar}').glob, '.{foo,bar}');
+
+    assert.equal(parse('a/b/*.{foo,bar}').base, 'a/b');
+    assert.equal(parse('a/b/*.{foo,bar}').glob, '*.{foo,bar}');
+
+    assert.equal(parse('a/**/b/*.{foo,bar}').base, 'a');
+    assert.equal(parse('a/**/b/*.{foo,bar}').glob, '**/b/*.{foo,bar}');
+
+    assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').base, 'a/b');
+    assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').glob, '{c,.gitignore,{a,b}}/{a,b}/abc.foo.js');
+
+    assert.equal(parse('a/b/{c,d}/').base, 'a/b');
+    assert.equal(parse('a/b/{c,d}/').glob, '{c,d}/');
+
+    assert.equal(parse('a/b/{c,d}/e/f.g').base, 'a/b');
+    assert.equal(parse('a/b/{c,d}/e/f.g').glob, '{c,d}/e/f.g');
+
+    assert.equal(parse('.a*').base, '.');
+    assert.equal(parse('.a*').glob, '.a*');
+
+    assert.equal(parse('.b*').base, '.');
+    assert.equal(parse('.b*').glob, '.b*');
+
+    assert.equal(parse('/*').base, '/');
+    assert.equal(parse('/*').glob, '*');
+
+    assert.equal(parse('a/***').base, 'a');
+    assert.equal(parse('a/***').glob, '***');
+
+    assert.equal(parse('a/**/b/*.{foo,bar}').base, 'a');
+    assert.equal(parse('a/**/b/*.{foo,bar}').glob, '**/b/*.{foo,bar}');
+
+    assert.equal(parse('a/**/c/*').base, 'a');
+    assert.equal(parse('a/**/c/*').glob, '**/c/*');
+
+    assert.equal(parse('a/**/c/*.md').base, 'a');
+    assert.equal(parse('a/**/c/*.md').glob, '**/c/*.md');
+
+    assert.equal(parse('a/**/e').base, 'a');
+    assert.equal(parse('a/**/e').glob, '**/e');
+
+    assert.equal(parse('a/**/j/**/z/*.md').base, 'a');
+    assert.equal(parse('a/**/j/**/z/*.md').glob, '**/j/**/z/*.md');
+
+    assert.equal(parse('a/**/z/*.md').base, 'a');
+    assert.equal(parse('a/**/z/*.md').glob, '**/z/*.md');
+
+    assert.equal(parse('a/**c*').base, 'a');
+    assert.equal(parse('a/**c*').glob, '**c*');
+
+    assert.equal(parse('a/**c/*').base, 'a');
+    assert.equal(parse('a/**c/*').glob, '**c/*');
+
+    assert.equal(parse('a/*/*/e').base, 'a');
+    assert.equal(parse('a/*/*/e').glob, '*/*/e');
+
+    assert.equal(parse('a/*/c/*.md').base, 'a');
+    assert.equal(parse('a/*/c/*.md').glob, '*/c/*.md');
+
+    assert.equal(parse('a/b/**/c{d,e}/**/xyz.md').base, 'a/b');
+    assert.equal(parse('a/b/**/c{d,e}/**/xyz.md').glob, '**/c{d,e}/**/xyz.md');
+
+    assert.equal(parse('a/b/**/e').base, 'a/b');
+    assert.equal(parse('a/b/**/e').glob, '**/e');
+
+    assert.equal(parse('a/b/*.{foo,bar}').base, 'a/b');
+    assert.equal(parse('a/b/*.{foo,bar}').glob, '*.{foo,bar}');
+
+    assert.equal(parse('a/b/*/e').base, 'a/b');
+    assert.equal(parse('a/b/*/e').glob, '*/e');
+
+    assert.equal(parse('a/b/c/*').base, 'a/b/c');
+    assert.equal(parse('a/b/c/*').glob, '*');
+
+    assert.equal(parse('a/b/c/*.md').base, 'a/b/c');
+    assert.equal(parse('a/b/c/*.md').glob, '*.md');
+
+    assert.equal(parse('a/b/c/.*.md').base, 'a/b/c');
+    assert.equal(parse('a/b/c/.*.md').glob, '.*.md');
+
+    assert.equal(parse('b/*/*/*').base, 'b');
+    assert.equal(parse('b/*/*/*').glob, '*/*/*');
+  });
+
+  it('file extensions:', function () {
+    assert.equal(parse('.md').base, '.');
+    assert.equal(parse('.md').glob, '.md');
+  });
+
+  it('negation pattern:', function () {
+    assert.equal(parse('!*.min.js').base, '.');
+    assert.equal(parse('!*.min.js').glob, '!*.min.js');
+
+    assert.equal(parse('!foo').base, '.');
+    assert.equal(parse('!foo').glob, '!foo');
+
+    assert.equal(parse('a/b/c/!foo').base, 'a/b/c');
+    assert.equal(parse('a/b/c/!foo').glob, '!foo');
+
+    assert.equal(parse('!foo/(a|b).min.js').base, '.');
+    assert.equal(parse('!foo/(a|b).min.js').glob, '!foo/(a|b).min.js');
+
+    assert.equal(parse('!foo/[a-b].min.js').base, '.');
+    assert.equal(parse('!foo/[a-b].min.js').glob, '!foo/[a-b].min.js');
+
+    assert.equal(parse('!foo/{a,b}.min.js').base, '.');
+    assert.equal(parse('!foo/{a,b}.min.js').glob, '!foo/{a,b}.min.js');
+  });
+
+  describe('braces:', function () {
+    it('should know when a base cannot be extracted:', function () {
+      assert.equal(parse('/a/b/{c,/foo.js}/e.f.g/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,/foo.js}/e.f.g/').glob, '{c,/foo.js}/e.f.g/');
+
+      assert.equal(parse('{a/b/c.js,/a/b/{c,/foo.js}/e.f.g/}').base, '.');
+      assert.equal(parse('{a/b/c.js,/a/b/{c,/foo.js}/e.f.g/}').glob, '{a/b/c.js,/a/b/{c,/foo.js}/e.f.g/}');
+
+      assert.equal(parse('/a/b/{c,d}/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/').glob, '{c,d}/');
+
+      assert.equal(parse('/a/b/{c,d}/*.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.js').glob, '{c,d}/*.js');
+
+      assert.equal(parse('/a/b/{c,d}/*.min.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').glob, '{c,d}/*.min.js');
+
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').glob, '{c,d}/e.f.g/');
+
+      assert.equal(parse('{.,*}').base, '.');
+      assert.equal(parse('{.,*}').glob, '{.,*}');
+    });
+
+    it('should work when the basename has braces:', function () {
+      assert.equal(parse('a/b/.{c,.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/.{c,.gitignore}').glob, '.{c,.gitignore}');
+
+      assert.equal(parse('a/b/.{c,/.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/.{c,/.gitignore}').glob, '.{c,/.gitignore}');
+
+      assert.equal(parse('a/b/.{foo,bar}').base, 'a/b');
+      assert.equal(parse('a/b/.{foo,bar}').glob, '.{foo,bar}');
+
+      assert.equal(parse('a/b/{c,.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore}').glob, '{c,.gitignore}');
+
+      assert.equal(parse('a/b/{c,/.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,/.gitignore}').glob, '{c,/.gitignore}');
+
+      assert.equal(parse('a/b/{c,/gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,/gitignore}').glob, '{c,/gitignore}');
+
+      assert.equal(parse('a/b/{c,d}').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}').glob, '{c,d}');
+    });
+
+    it('should work when the dirname has braces:', function () {
+      assert.equal(parse('a/b/{c,./d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').glob, '{c,./d}/e/f.g');
+
+      assert.equal(parse('a/b/{c,./d}/e/f.min.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,./d}/e/f.min.g').glob, '{c,./d}/e/f.min.g');
+
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').glob, '{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js');
+
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').glob, '{c,.gitignore,{a,b}}/{a,b}/*.foo.js');
+
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').glob, '{c,.gitignore,{a,b}}/{a,b}/abc.foo.js');
+
+      assert.equal(parse('a/b/{c,/d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').glob, '{c,/d}/e/f.g');
+
+      assert.equal(parse('a/b/{c,/d}/e/f.min.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,/d}/e/f.min.g').glob, '{c,/d}/e/f.min.g');
+
+      assert.equal(parse('a/b/{c,d}/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/').glob, '{c,d}/');
+
+      assert.equal(parse('a/b/{c,d}/*.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.js').glob, '{c,d}/*.js');
+
+      assert.equal(parse('a/b/{c,d}/*.min.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.min.js').glob, '{c,d}/*.min.js');
+
+      assert.equal(parse('a/b/{c,d}/e.f.g/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').glob, '{c,d}/e.f.g/');
+
+      assert.equal(parse('a/b/{c,d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e/f.g').glob, '{c,d}/e/f.g');
+
+      assert.equal(parse('a/b/{c,d}/e/f.min.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e/f.min.g').glob, '{c,d}/e/f.min.g');
+
+      assert.equal(parse('foo/{a,b}.min.js').base, 'foo');
+      assert.equal(parse('foo/{a,b}.min.js').glob, '{a,b}.min.js');
+    });
+  });
+
+  it('character classes:', function () {
+    assert.equal(parse('/[.]bashrc').base, '/');
+    assert.equal(parse('/[.]bashrc').glob, '[.]bashrc');
+    assert.equal(parse('/[.]bashrc').path.basename, '[.]bashrc');
+    assert.equal(parse('/[.]bashrc').path.filename, '[.]bashrc');
+    assert.equal(parse('/[.]bashrc').path.extname, '');
+
+    assert.equal(parse('[a-c]b*').base, '.');
+    assert.equal(parse('[a-c]b*').glob, '[a-c]b*');
+
+    assert.equal(parse('[a-j]*[^c]').base, '.');
+    assert.equal(parse('[a-j]*[^c]').glob, '[a-j]*[^c]');
+
+    assert.equal(parse('[a-j]*[^c]b/c').base, '.');
+    assert.equal(parse('[a-j]*[^c]b/c').glob, '[a-j]*[^c]b/c');
+
+    assert.equal(parse('[a-j]*[^c]bc').base, '.');
+    assert.equal(parse('[a-j]*[^c]bc').glob, '[a-j]*[^c]bc');
+
+    assert.equal(parse('[ab][ab]').base, '.');
+    assert.equal(parse('[ab][ab]').glob, '[ab][ab]');
+
+    assert.equal(parse('foo/[a-b].min.js').base, 'foo');
+    assert.equal(parse('foo/[a-b].min.js').glob, '[a-b].min.js');
+  });
+
+  it('qmarks:', function () {
+    assert.equal(parse('?').base, '.');
+    assert.equal(parse('?').glob, '?');
+
+    assert.equal(parse('?/?').base, '.');
+    assert.equal(parse('?/?').glob, '?/?');
+
+    assert.equal(parse('??').base, '.');
+    assert.equal(parse('??').glob, '??');
+
+    assert.equal(parse('???').base, '.');
+    assert.equal(parse('???').glob, '???');
+
+    assert.equal(parse('?a').base, '.');
+    assert.equal(parse('?a').glob, '?a');
+
+    assert.equal(parse('?b').base, '.');
+    assert.equal(parse('?b').glob, '?b');
+
+    assert.equal(parse('a?b').base, '.');
+    assert.equal(parse('a?b').glob, 'a?b');
+
+    assert.equal(parse('a/?/c.js').base, 'a');
+    assert.equal(parse('a/?/c.js').glob, '?/c.js');
+
+    assert.equal(parse('a/?/c.md').base, 'a');
+    assert.equal(parse('a/?/c.md').glob, '?/c.md');
+
+    assert.equal(parse('a/?/c/?/*/f.js').base, 'a');
+    assert.equal(parse('a/?/c/?/*/f.js').glob, '?/c/?/*/f.js');
+
+    assert.equal(parse('a/?/c/?/*/f.md').base, 'a');
+    assert.equal(parse('a/?/c/?/*/f.md').glob, '?/c/?/*/f.md');
+
+    assert.equal(parse('a/?/c/?/e.js').base, 'a');
+    assert.equal(parse('a/?/c/?/e.js').glob, '?/c/?/e.js');
+
+    assert.equal(parse('a/?/c/?/e.md').base, 'a');
+    assert.equal(parse('a/?/c/?/e.md').glob, '?/c/?/e.md');
+
+    assert.equal(parse('a/?/c/???/e.js').base, 'a');
+    assert.equal(parse('a/?/c/???/e.js').glob, '?/c/???/e.js');
+
+    assert.equal(parse('a/?/c/???/e.md').base, 'a');
+    assert.equal(parse('a/?/c/???/e.md').glob, '?/c/???/e.md');
+
+    assert.equal(parse('a/??/c.js').base, 'a');
+    assert.equal(parse('a/??/c.js').glob, '??/c.js');
+
+    assert.equal(parse('a/??/c.md').base, 'a');
+    assert.equal(parse('a/??/c.md').glob, '??/c.md');
+
+    assert.equal(parse('a/???/c.js').base, 'a');
+    assert.equal(parse('a/???/c.js').glob, '???/c.js');
+
+    assert.equal(parse('a/???/c.md').base, 'a');
+    assert.equal(parse('a/???/c.md').glob, '???/c.md');
+
+    assert.equal(parse('a/????/c.js').base, 'a');
+    assert.equal(parse('a/????/c.js').glob, '????/c.js');
+  });
+
+  it('non-glob pattern:', function () {
+    assert.equal(parse('').base, '.');
+    assert.equal(parse('').glob, '');
+
+    assert.equal(parse('.').base, '.');
+    assert.equal(parse('.').glob, '.');
+
+    assert.equal(parse('a').base, '.');
+    assert.equal(parse('a').glob, 'a');
+
+    assert.equal(parse('.a').base, '.');
+    assert.equal(parse('.a').glob, '.a');
+
+    assert.equal(parse('/a').base, '/');
+    assert.equal(parse('/a').glob, 'a');
+
+    assert.equal(parse('/a/b/c').base, '/a/b');
+    assert.equal(parse('/a/b/c').glob, 'c');
+
+    assert.equal(parse('/a/b/c/').base, '/a/b/c/');
+    assert.equal(parse('/a/b/c/').glob, '');
+
+    assert.equal(parse('a/b/c/').base, 'a/b/c/');
+    assert.equal(parse('a/b/c/').glob, '');
+
+    assert.equal(parse('a.min.js').base, '.');
+    assert.equal(parse('a.min.js').glob, 'a.min.js');
+
+    assert.equal(parse('a/.x.md').base, 'a');
+    assert.equal(parse('a/.x.md').glob, '.x.md');
+
+    assert.equal(parse('a/b/.gitignore').base, 'a/b');
+    assert.equal(parse('a/b/.gitignore').glob, '.gitignore');
+
+    assert.equal(parse('a/b/c/d.md').base, 'a/b/c');
+    assert.equal(parse('a/b/c/d.md').glob, 'd.md');
+
+    assert.equal(parse('a/b/c/d.e.f/g.min.js').base, 'a/b/c/d.e.f');
+    assert.equal(parse('a/b/c/d.e.f/g.min.js').glob, 'g.min.js');
+
+    assert.equal(parse('a/b/.git').base, 'a/b');
+    assert.equal(parse('a/b/.git').glob, '.git');
+
+    assert.equal(parse('a/b/.git/').base, 'a/b/.git/');
+    assert.equal(parse('a/b/.git/').glob, '');
+
+    assert.equal(parse('a/b/.gitignore').base, 'a/b');
+    assert.equal(parse('a/b/.gitignore').glob, '.gitignore');
+
+    assert.equal(parse('a/b/c').base, 'a/b');
+    assert.equal(parse('a/b/c').glob, 'c');
+
+    assert.equal(parse('a/b/c.d/e.md').base, 'a/b/c.d');
+    assert.equal(parse('a/b/c.d/e.md').glob, 'e.md');
+
+    assert.equal(parse('a/b/c.md').base, 'a/b');
+    assert.equal(parse('a/b/c.md').glob, 'c.md');
+
+    assert.equal(parse('a/b/c.min.js').base, 'a/b');
+    assert.equal(parse('a/b/c.min.js').glob, 'c.min.js');
+
+    assert.equal(parse('a/b/c/').base, 'a/b/c/');
+    assert.equal(parse('a/b/c/').glob, '');
+
+    assert.equal(parse('a/b/c/d.e.f/g.min.js').base, 'a/b/c/d.e.f');
+    assert.equal(parse('a/b/c/d.e.f/g.min.js').glob, 'g.min.js');
+
+    assert.equal(parse('a/b/c/d.md').base, 'a/b/c');
+    assert.equal(parse('a/b/c/d.md').glob, 'd.md');
+
+    assert.equal(parse('a/b/git/').base, 'a/b/git/');
+    assert.equal(parse('a/b/git/').glob, '');
+
+    assert.equal(parse('aa').base, '.');
+    assert.equal(parse('aa').glob, 'aa');
+
+    assert.equal(parse('c.md').base, '.');
+    assert.equal(parse('c.md').glob, 'c.md');
+  });
+});
+
+describe('path parts:', function () {
+  describe('complex patterns:', function () {
+    it('should get a dirname:', function () {
+      assert.equal(parse('*.min.js').base, '.');
+      assert.equal(parse('/a/b/c').base, '/a/b');
+      assert.equal(parse('/a/b/c/').base, '/a/b/c/');
+      assert.equal(parse('/a/b/{c,d}/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,/foo.js}/e.f.g/').base, '/a/b');
+      assert.equal(parse('./{a/b/{c,/foo.js}/e.f.g}').base, '.');
+      assert.equal(parse('[a-c]b*').base, '.');
+      assert.equal(parse('[a-j]*[^c]').base, '.');
+      assert.equal(parse('[a-j]*[^c]b/c').base, '.');
+      assert.equal(parse('[a-j]*[^c]bc').base, '.');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,/.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,/gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.min.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a').base, '.');
+      assert.equal(parse('a/**/b/*.{foo,bar}').base, 'a');
+      assert.equal(parse('a/b/*.{foo,bar}').base, 'a/b');
+      assert.equal(parse('a/b/.gitignore').base, 'a/b');
+      assert.equal(parse('a/b/.{c,.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/.{c,/.gitignore}').base, 'a/b');
+      assert.equal(parse('a/b/.{foo,bar}').base, 'a/b');
+      assert.equal(parse('a/b/c').base, 'a/b');
+      assert.equal(parse('a/b/c.d/e.md').base, 'a/b/c.d');
+      assert.equal(parse('a/b/c.md').base, 'a/b');
+      assert.equal(parse('a/b/c.min.js').base, 'a/b');
+      assert.equal(parse('a/b/c/').base, 'a/b/c/');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').base, 'a/b/c/d.e.f');
+      assert.equal(parse('a/b/c/d.md').base, 'a/b/c');
+      assert.equal(parse('c.md').base, '.');
+    });
+
+    it('should get a basename:', function () {
+      assert.equal(parse('*.min.js').path.basename, '*.min.js');
+      assert.equal(parse('/a/b/{c,d}/').path.basename, '');
+      assert.equal(parse('/a/b/{c,d}/*.js').path.basename, '*.js');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.basename, '*.min.js');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.basename, '');
+      assert.equal(parse('[a-j]*[^c]').path.basename, '[a-j]*[^c]');
+      assert.equal(parse('[a-j]*[^c]bc').path.basename, '[a-j]*[^c]bc');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.basename, '*.{foo,bar}');
+      assert.equal(parse('a/b/*.{foo,bar}').path.basename, '*.{foo,bar}');
+      assert.equal(parse('a/b/.{c,.gitignore}').path.basename, '.{c,.gitignore}');
+      assert.equal(parse('a/b/.{c,/.gitignore}').path.basename, '.{c,/.gitignore}');
+      assert.equal(parse('a/b/.{foo,bar}').path.basename, '.{foo,bar}');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.basename, 'f.g');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.basename, 'abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.basename, '*.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.basename, 'abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore}').path.basename, '{c,.gitignore}');
+      assert.equal(parse('a/b/{c,/.gitignore}').path.basename, '{c,/.gitignore}');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.basename, 'f.g');
+      assert.equal(parse('a/b/{c,/gitignore}').path.basename, '{c,/gitignore}');
+      assert.equal(parse('a/b/{c,d}').path.basename, '{c,d}');
+      assert.equal(parse('a/b/{c,d}/').path.basename, '');
+      assert.equal(parse('a/b/{c,d}/*.js').path.basename, '*.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.basename, '*.min.js');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.basename, '');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.basename, 'f.g');
+    });
+
+    it('should get a filename:', function () {
+      assert.equal(parse('*.min.js').path.filename, '*');
+      assert.equal(parse('/a/b/c').path.filename, 'c');
+      assert.equal(parse('/a/b/c/').path.filename, '');
+      assert.equal(parse('/a/b/{c,d}/').path.filename, '');
+      assert.equal(parse('/a/b/{c,d}/*.js').path.filename, '*');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.filename, '*');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.filename, '');
+      assert.equal(parse('[a-c]b*').path.filename, '[a-c]b*');
+      assert.equal(parse('a').path.filename, 'a');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.filename, '*');
+      assert.equal(parse('a/b/*.{foo,bar}').path.filename, '*');
+      assert.equal(parse('a/b/.gitignore').path.filename, '');
+      assert.equal(parse('a/b/.{c,.gitignore}').path.filename, '');
+      assert.equal(parse('a/b/.{c,/.gitignore}').path.filename, '');
+      assert.equal(parse('a/b/.{foo,bar}').path.filename, '');
+      assert.equal(parse('a/b/c').path.filename, 'c');
+      assert.equal(parse('a/b/c.d/e.md').path.filename, 'e');
+      assert.equal(parse('a/b/c.md').path.filename, 'c');
+      assert.equal(parse('a/b/c.min.js').path.filename, 'c');
+      assert.equal(parse('a/b/c/').path.filename, '');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.filename, 'g');
+      assert.equal(parse('a/b/c/d.md').path.filename, 'd');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.filename, 'abc');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.filename, 'abc');
+      assert.equal(parse('a/b/{c,.gitignore}').path.filename, '{c,.gitignore}');
+      assert.equal(parse('a/b/{c,/.gitignore}').path.filename, '{c,/.gitignore}');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('a/b/{c,/gitignore}').path.filename, '{c,/gitignore}');
+      assert.equal(parse('a/b/{c,d}').path.filename, '{c,d}');
+      assert.equal(parse('a/b/{c,d}/').path.filename, '');
+      assert.equal(parse('a/b/{c,d}/*.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.filename, '');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('c.md').path.filename, 'c');
+    });
+
+    it('should get an extname:', function () {
+      assert.equal(parse('*.min.js').path.extname, '.min.js');
+      assert.equal(parse('/a/b/c').path.extname, '');
+      assert.equal(parse('/a/b/c/').path.extname, '');
+      assert.equal(parse('/a/b/{c,d}/').path.extname, '');
+      assert.equal(parse('/a/b/{c,d}/*.js').path.extname, '.js');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.extname, '.min.js');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.extname, '');
+      assert.equal(parse('[a-c]b*').path.extname, '');
+      assert.equal(parse('[a-j]*[^c]bc').path.extname, '');
+      assert.equal(parse('a').path.extname, '');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.extname, '.{foo,bar}');
+      assert.equal(parse('a/b/*.{foo,bar}').path.extname, '.{foo,bar}');
+      assert.equal(parse('a/b/.gitignore').path.extname, '.gitignore');
+      assert.equal(parse('a/b/.{c,.gitignore}').path.extname, '.{c,.gitignore}');
+      assert.equal(parse('a/b/.{c,/.gitignore}').path.extname, '.{c,/.gitignore}');
+      assert.equal(parse('a/b/.{foo,bar}').path.extname, '.{foo,bar}');
+      assert.equal(parse('a/b/c').path.extname, '');
+      assert.equal(parse('a/b/c.d/e.md').path.extname, '.md');
+      assert.equal(parse('a/b/c.md').path.extname, '.md');
+      assert.equal(parse('a/b/c.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/c/').path.extname, '');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/c/d.md').path.extname, '.md');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore}').path.extname, '');
+      assert.equal(parse('a/b/{c,/.gitignore}').path.extname, '');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('a/b/{c,/gitignore}').path.extname, '');
+      assert.equal(parse('a/b/{c,d}').path.extname, '');
+      assert.equal(parse('a/b/{c,d}/').path.extname, '');
+      assert.equal(parse('a/b/{c,d}/*.js').path.extname, '.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.extname, '');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('c.md').path.extname, '.md');
+    });
+
+    it('should match a path with an extension:', function () {
+      assert.equal(parse('a/b/c.md').glob, 'c.md');
+      assert.equal(parse('a/b/c.md').base, 'a/b');
+      assert.equal(parse('a/b/c.md').path.basename, 'c.md');
+      assert.equal(parse('a/b/c.md').path.filename, 'c');
+      assert.equal(parse('a/b/c.md').path.extname, '.md');
+      assert.equal(parse('a/b/c.md').path.ext, 'md');
+      assert.equal(parse('c.md').glob, 'c.md');
+      assert.equal(parse('c.md').base, '.');
+      assert.equal(parse('c.md').path.basename, 'c.md');
+      assert.equal(parse('c.md').path.filename, 'c');
+      assert.equal(parse('c.md').path.extname, '.md');
+      assert.equal(parse('c.md').path.ext, 'md');
+    });
+
+    it('should match a path with multiple extensions:', function () {
+      assert.equal(parse('a/b/c.min.js').glob, 'c.min.js');
+      assert.equal(parse('a/b/c.min.js').base, 'a/b');
+      assert.equal(parse('a/b/c.min.js').path.basename, 'c.min.js');
+      assert.equal(parse('a/b/c.min.js').path.filename, 'c');
+      assert.equal(parse('a/b/c.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/c.min.js').path.ext, 'js');
+    });
+
+    it('should work with paths that have dots in the dirname:', function () {
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').glob, 'g.min.js');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').base, 'a/b/c/d.e.f');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.basename, 'g.min.js');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.filename, 'g');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/c/d.e.f/g.min.js').path.ext, 'js');
+    });
+
+    it('should match a path without an extension:', function () {
+      assert.equal(parse('a').glob, 'a');
+      assert.equal(parse('a').base, '.');
+      assert.equal(parse('a').path.filename, 'a');
+      assert.equal(parse('a').path.basename, 'a');
+      assert.equal(parse('a').path.extname, '');
+    });
+
+    it('should match a file path ending with an extension:', function () {
+      assert.equal(parse('a/b/c/d.md').glob, 'd.md');
+      assert.equal(parse('a/b/c/d.md').base, 'a/b/c');
+      assert.equal(parse('a/b/c/d.md').path.basename, 'd.md');
+      assert.equal(parse('a/b/c/d.md').path.filename, 'd');
+      assert.equal(parse('a/b/c/d.md').path.extname, '.md');
+      assert.equal(parse('a/b/c/d.md').path.ext, 'md');
+
+      assert.equal(parse('a/b/c.d/e.md').glob, 'e.md');
+      assert.equal(parse('a/b/c.d/e.md').base, 'a/b/c.d');
+      assert.equal(parse('a/b/c.d/e.md').path.basename, 'e.md');
+      assert.equal(parse('a/b/c.d/e.md').path.filename, 'e');
+      assert.equal(parse('a/b/c.d/e.md').path.extname, '.md');
+      assert.equal(parse('a/b/c.d/e.md').path.ext, 'md');
+    });
+
+  });
+
+  describe('glob characters:', function () {
+    it('should match when the basename starts with a star:', function () {
+      assert.equal(parse('a/b/*.{foo,bar}').glob, '*.{foo,bar}');
+      assert.equal(parse('a/b/*.{foo,bar}').base, 'a/b');
+      assert.equal(parse('a/b/*.{foo,bar}').path.basename, '*.{foo,bar}');
+      assert.equal(parse('a/b/*.{foo,bar}').path.filename, '*');
+      assert.equal(parse('a/b/*.{foo,bar}').path.extname, '.{foo,bar}');
+      assert.equal(parse('a/b/*.{foo,bar}').path.ext, '{foo,bar}');
+    });
+
+    it('should match with globstars:', function () {
+      assert.equal(parse('a/**/b/*.{foo,bar}').glob, '**/b/*.{foo,bar}');
+      assert.equal(parse('a/**/b/*.{foo,bar}').base, 'a');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.basename, '*.{foo,bar}');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.filename, '*');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.extname, '.{foo,bar}');
+      assert.equal(parse('a/**/b/*.{foo,bar}').path.ext, '{foo,bar}');
+    });
+
+    it('should match complex patterns:', function () {
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').glob, '{c,.gitignore,{a,b}}/{a,b}/abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.basename, 'abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.filename, 'abc');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/abc.foo.js').path.ext, 'js');
+
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').glob, '{c,.gitignore,{a,b}}/{a,b}/*.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.basename, '*.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,b}}/{a,b}/*.foo.js').path.ext, 'js');
+
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').glob, '{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.basename, 'abc.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.filename, 'abc');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.extname, '.foo.js');
+      assert.equal(parse('a/b/{c,.gitignore,{a,./b}}/{a,b}/abc.foo.js').path.ext, 'js');
+    });
+  });
+
+  describe('should parse a glob:', function () {
+    it('ending with a brace pattern:', function () {
+      assert.equal(parse('a/b/{c,d}').glob, '{c,d}');
+      assert.equal(parse('a/b/{c,d}').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}').path.basename, '{c,d}');
+      assert.equal(parse('a/b/{c,d}').path.filename, '{c,d}');
+      assert.equal(parse('a/b/{c,d}').path.extname, '');
+      assert.equal(parse('a/b/{c,d}').path.ext, '');
+    });
+
+    it('with a brace pattern in the dirname:', function () {
+      assert.equal(parse('a/b/{c,d}/*.js').orig, 'a/b/{c,d}/*.js');
+      assert.equal(parse('a/b/{c,d}/*.js').glob, '{c,d}/*.js');
+      assert.equal(parse('a/b/{c,d}/*.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.js').path.basename, '*.js');
+      assert.equal(parse('a/b/{c,d}/*.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,d}/*.js').path.extname, '.js');
+      assert.equal(parse('a/b/{c,d}/*.js').path.ext, 'js');
+
+      assert.equal(parse('a/b/{c,d}/').orig, 'a/b/{c,d}/');
+      assert.equal(parse('a/b/{c,d}/').glob, '{c,d}/');
+      assert.equal(parse('a/b/{c,d}/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/').path.basename, '');
+      assert.equal(parse('a/b/{c,d}/').path.filename, '');
+      assert.equal(parse('a/b/{c,d}/').path.extname, '');
+      assert.equal(parse('a/b/{c,d}/').path.ext, '');
+
+      assert.equal(parse('a/b/{c,d}/e.f.g/').orig, 'a/b/{c,d}/e.f.g/');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').glob, '{c,d}/e.f.g/');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.basename, '');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.filename, '');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.extname, '');
+      assert.equal(parse('a/b/{c,d}/e.f.g/').path.ext, '');
+
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').orig, '/a/b/{c,d}/e.f.g/');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').glob, '{c,d}/e.f.g/');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.basename, '');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.filename, '');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.extname, '');
+      assert.equal(parse('/a/b/{c,d}/e.f.g/').path.ext, '');
+
+      assert.equal(parse('a/b/{c,d}/*.min.js').orig, 'a/b/{c,d}/*.min.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').glob, '{c,d}/*.min.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.basename, '*.min.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.filename, '*');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.ext, 'js');
+
+      assert.equal(parse('*.min.js').orig, '*.min.js');
+      assert.equal(parse('*.min.js').glob, '*.min.js');
+      assert.equal(parse('*.min.js').base, '.');
+      assert.equal(parse('*.min.js').path.basename, '*.min.js');
+      assert.equal(parse('*.min.js').path.filename, '*');
+      assert.equal(parse('*.min.js').path.extname, '.min.js');
+      assert.equal(parse('*.min.js').path.ext, 'js');
+
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.extname, '.min.js');
+      assert.equal(parse('a/b/{c,d}/*.min.js').path.ext, 'js');
+
+      assert.equal(parse('[a-j]*[^c]').glob, '[a-j]*[^c]');
+      assert.equal(parse('[a-j]*[^c]').base, '.');
+      assert.equal(parse('[a-j]*[^c]').path.dirname, '');
+      assert.equal(parse('[a-j]*[^c]').path.basename, '[a-j]*[^c]');
+      assert.equal(parse('[a-j]*[^c]b/c').glob, '[a-j]*[^c]b/c');
+      assert.equal(parse('[a-j]*[^c]b/c').base, '.');
+      assert.equal(parse('[a-j]*[^c]bc').glob, '[a-j]*[^c]bc');
+      assert.equal(parse('[a-j]*[^c]bc').base, '.');
+      assert.equal(parse('[a-j]*[^c]bc').path.basename, '[a-j]*[^c]bc');
+    });
+
+    it('ending with a slash:', function () {
+      assert.equal(parse('a/b/{c,d}/').orig, 'a/b/{c,d}/');
+      assert.equal(parse('a/b/{c,d}/').glob, '{c,d}/');
+      assert.equal(parse('a/b/{c,d}/').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/').path.basename, '');
+      assert.equal(parse('a/b/{c,d}/').path.filename, '');
+      assert.equal(parse('a/b/{c,d}/').path.extname, '');
+    });
+
+    it('beginning with a slash:', function () {
+      assert.equal(parse('/a/b/{c,d}/').orig, '/a/b/{c,d}/');
+      assert.equal(parse('/a/b/{c,d}/').glob, '{c,d}/');
+      assert.equal(parse('/a/b/{c,d}/').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/').path.basename, '');
+      assert.equal(parse('/a/b/{c,d}/').path.filename, '');
+      assert.equal(parse('/a/b/{c,d}/').path.extname, '');
+    });
+
+    it('with a brace pattern in the dirname:', function () {
+      assert.equal(parse('a/b/{c,d}/e/f.g').orig, 'a/b/{c,d}/e/f.g');
+      assert.equal(parse('a/b/{c,d}/e/f.g').glob, '{c,d}/e/f.g');
+      assert.equal(parse('a/b/{c,d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.basename, 'f.g');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('a/b/{c,d}/e/f.g').path.ext, 'g');
+
+      assert.equal(parse('a/b/{c,./d}/e/f.g').orig, 'a/b/{c,./d}/e/f.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').glob, '{c,./d}/e/f.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.basename, 'f.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('a/b/{c,./d}/e/f.g').path.ext, 'g');
+
+      assert.equal(parse('a/b/{c,/d}/e/f.g').orig, 'a/b/{c,/d}/e/f.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').glob, '{c,/d}/e/f.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').base, 'a/b');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.basename, 'f.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.filename, 'f');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.extname, '.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.min.g').path.extname, '.min.g');
+      assert.equal(parse('a/b/{c,/d}/e/f.g').path.ext, 'g');
+    });
+  });
+
+  describe('when the glob pattern has braces:', function () {
+    it('should track the original pattern', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').orig, '/a/b/{c,d}/*.js');
+      assert.equal(parse('/a/b/{c,d}/*.js').orig, '/a/b/{c,d}/*.js');
+    });
+
+    it('should extract the dirname', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').base, '/a/b');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').glob, '{c,d}/*.min.js');
+    });
+
+    it('should extract the basename', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').path.basename, '*.js');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.basename, '*.min.js');
+    });
+
+    it('should extract the filename', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').path.filename, '*');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.filename, '*');
+    });
+
+    it('should extract extname', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').path.extname, '.js');
+      assert.equal(parse('/a/b/{c,d}/*.min.js').path.extname, '.min.js');
+    });
+
+    it('should extract ext', function () {
+      assert.equal(parse('/a/b/{c,d}/*.js').path.ext, 'js');
+      assert.equal(parse('/a/b/{c,d}/*.js').path.ext, 'js');
+    });
+  });
+
+  describe('trailing and leading slashs:', function () {
+    it('should work without a trailing slash:', function () {
+      assert.equal(parse('a/b/c').glob, 'c');
+      assert.equal(parse('a/b/c').base, 'a/b');
+      assert.equal(parse('a/b/c').path.filename, 'c');
+      assert.equal(parse('a/b/c').path.basename, 'c');
+      assert.equal(parse('a/b/c').path.extname, '');
+    });
+
+    it('should work with a trailing slash:', function () {
+      assert.equal(parse('a/b/c/').glob, '');
+      assert.equal(parse('a/b/c/').base, 'a/b/c/');
+      assert.equal(parse('a/b/c/').path.filename, '');
+      assert.equal(parse('a/b/c/').path.basename, '');
+      assert.equal(parse('a/b/c/').path.extname, '');
+    });
+
+    it('should work with a leading slash:', function () {
+      assert.equal(parse('/a/b/c').glob, 'c');
+      assert.equal(parse('/a/b/c').base, '/a/b');
+      assert.equal(parse('/a/b/c').path.filename, 'c');
+      assert.equal(parse('/a/b/c').path.basename, 'c');
+      assert.equal(parse('/a/b/c').path.extname, '');
+    });
+
+    it('should work with both trailing and leading slashes:', function () {
+      assert.equal(parse('/a/b/c/').glob, '');
+      assert.equal(parse('/a/b/c/').base, '/a/b/c/');
+      assert.equal(parse('/a/b/c/').path.filename, '');
+      assert.equal(parse('/a/b/c/').path.basename, '');
+      assert.equal(parse('/a/b/c/').path.extname, '');
+    });
+  });
+
+  describe('dotfile and dotdir:', function () {
+    describe('dotfiles:', function () {
+      it('should know when the file is a dotfile:', function () {
+        assert.equal(parse('a/b/.gitignore').is.dotfile, true);
+        assert.equal(parse('a/b/.gitignore').is.dotfile, true);
+        assert.equal(parse('a/b/.git').is.dotfile, true);
+        assert.equal(parse('a/b/.gitignore').is.dotdir, false);
+        assert.equal(parse('a/b/.git').is.dotdir, false);
+      });
+
+      it('should work brace patterns:', function () {
+        assert.equal(parse('a/b/.{foo,bar}').orig, 'a/b/.{foo,bar}');
+        assert.equal(parse('a/b/.{foo,bar}').glob, '.{foo,bar}');
+        assert.equal(parse('a/b/.{foo,bar}').base, 'a/b');
+        assert.equal(parse('a/b/.{c,/.gitignore}').orig, 'a/b/.{c,/.gitignore}');
+        assert.equal(parse('a/b/.{c,/.gitignore}').glob, '.{c,/.gitignore}');
+        assert.equal(parse('a/b/.{c,/.gitignore}').base, 'a/b');
+        assert.equal(parse('a/b/.{c,.gitignore}').orig, 'a/b/.{c,.gitignore}');
+        assert.equal(parse('a/b/.{c,.gitignore}').glob, '.{c,.gitignore}');
+        assert.equal(parse('a/b/.{c,.gitignore}').base, 'a/b');
+      });
+    });
+
+    describe('dotdirs:', function () {
+      it('should know when the file is a dot-directory:', function () {
+        assert.equal(parse('a/b/.git/config').is.dotdir, true);
+        assert.equal(parse('a/b/.git/').is.dotdir, true);
+        assert.equal(parse('a/b/git/').is.dotdir, false);
+        assert.equal(parse('a/b/.git/').is.dotfile, false);
+      });
+
+      it('should work with braces in the dirname:', function () {
+        // assert.equal(parse('a/b/.{foo,bar}/foo.js').is.dotdir, true);
+        assert.equal(parse('a/b/.{foo,bar}/foo.js').orig, 'a/b/.{foo,bar}/foo.js');
+        assert.equal(parse('a/b/.{foo,bar}/foo.js').glob, '.{foo,bar}/foo.js');
+        assert.equal(parse('a/b/.{foo,bar}/foo.js').base, 'a/b');
+      });
+    });
+  });
+
+
+  it('should match character classes:', function () {
+    assert.equal(parse('[a-c]b*').glob, '[a-c]b*');
+    assert.equal(parse('[a-j]*[^c]').glob, '[a-j]*[^c]');
+    assert.equal(parse('[a-j]*[^c]').base, '.');
+    assert.equal(parse('[a-j]*[^c]b/c').glob, '[a-j]*[^c]b/c');
+    assert.equal(parse('[a-j]*[^c]bc').glob, '[a-j]*[^c]bc');
+  });
+
+  it('should work when a character class has trailing word characters:', function () {
+    assert.equal(parse('[a-c]b*').glob, '[a-c]b*');
+    assert.equal(parse('[a-c]b*').base, '.');
+    assert.equal(parse('[a-j]*[^c]bc').base, '.');
+  });
+});
+
+describe('when the glob has brace patterns:', function () {
+  it('should extract the `base` from the glob:', function () {
+    assert.equal(parse('a/b/{c,/.gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,/.gitignore}').glob, '{c,/.gitignore}');
+    assert.equal(parse('a/b/{c,/.gitignore}').orig, 'a/b/{c,/.gitignore}');
+
+    assert.equal(parse('a/b/{c,/gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,/gitignore}').glob, '{c,/gitignore}');
+
+    assert.equal(parse('a/b/{c,.gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,.gitignore}').glob, '{c,.gitignore}');
+  });
+
+  it('should work when the brace pattern has dots and slashes:', function () {
+    assert.equal(parse('a/b/{c,/.gitignore}').orig, 'a/b/{c,/.gitignore}');
+    assert.equal(parse('a/b/{c,/.gitignore}').glob, '{c,/.gitignore}');
+    assert.equal(parse('a/b/{c,/.gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,/.gitignore}').path.basename, '{c,/.gitignore}');
+    assert.equal(parse('a/b/{c,/.gitignore}').path.filename, '{c,/.gitignore}');
+    assert.equal(parse('a/b/{c,/.gitignore}').path.extname, '');
+
+    assert.equal(parse('a/b/{c,/gitignore}').orig, 'a/b/{c,/gitignore}');
+    assert.equal(parse('a/b/{c,/gitignore}').glob, '{c,/gitignore}');
+    assert.equal(parse('a/b/{c,/gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,/gitignore}').path.basename, '{c,/gitignore}');
+    assert.equal(parse('a/b/{c,/gitignore}').path.filename, '{c,/gitignore}');
+    assert.equal(parse('a/b/{c,/gitignore}').path.extname, '');
+
+    assert.equal(parse('a/b/{c,.gitignore}').orig, 'a/b/{c,.gitignore}');
+    assert.equal(parse('a/b/{c,.gitignore}').glob, '{c,.gitignore}');
+    assert.equal(parse('a/b/{c,.gitignore}').base, 'a/b');
+    assert.equal(parse('a/b/{c,.gitignore}').path.basename, '{c,.gitignore}');
+    assert.equal(parse('a/b/{c,.gitignore}').path.filename, '{c,.gitignore}');
+    assert.equal(parse('a/b/{c,.gitignore}').path.extname, '');
+    parse('a/b/c/**/*.{yml,json}')
+  });
+});

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



More information about the Pkg-javascript-commits mailing list