From e184907222d41cc8ea6834bd34f0f3fe9509022c Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 4 Sep 2017 12:40:07 -0700 Subject: [PATCH] Add micromatch.capture to return captured matches --- index.js | 38 +++++++++++++++++++++++++++ test/brackets.js | 4 +-- test/capture.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 test/capture.js diff --git a/index.js b/index.js index ad460e4f..fe29ca5f 100644 --- a/index.js +++ b/index.js @@ -516,6 +516,44 @@ micromatch.matcher = function matcher(pattern, options) { return fn; }; +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * var mm = require('micromatch'); + * mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js)); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `pattern` Glob pattern to use for matching. + * @param {String} `string` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * @api public + */ + +micromatch.capture = function(pattern, str, options) { + var re = micromatch.makeRe(pattern, extend({capture: true}, options)); + var unixify = utils.unixify(options); + + function match() { + return function(string) { + var match = re.exec(unixify(string)); + if (!match) { + return null; + } + + return match.slice(1); + }; + } + + var capture = memoize('capture', pattern, options, match); + return capture(str); +}; + /** * Create a regular expression from the given glob `pattern`. * diff --git a/test/brackets.js b/test/brackets.js index 36b9fc21..6e961819 100644 --- a/test/brackets.js +++ b/test/brackets.js @@ -22,8 +22,8 @@ describe('brackets', function() { assert.equal(create('[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'), '[a-zA-Z0-9a-zA-Z \\t\\x00-\\x1F\\x7F0-9\\x21-\\x7Ea-z\\x20-\\x7E \\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~ \\t\\r\\n\\v\\fA-ZA-Fa-f0-9]'); assert.equal(create('[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'), '[^a-zA-Z0-9a-zA-Z \\t\\x00-\\x1F\\x7F0-9a-z \\t\\r\\n\\v\\fA-ZA-Fa-f0-9]'); assert.equal(create('[a-c[:digit:]x-z]'), '[a-c0-9x-z]'); - assert.equal(create('[_[:alpha:]][_[:alnum:]][_[:alnum:]]*', {bash: false}), '[_a-zA-Z][_a-zA-Z0-9][_a-zA-Z0-9]*?(\\/|$)', []); - assert.equal(create('[_[:alpha:]][_[:alnum:]][_[:alnum:]]*'), '[_a-zA-Z][_a-zA-Z0-9][_a-zA-Z0-9][^/]*?(\\/|$)', []); + assert.equal(create('[_[:alpha:]][_[:alnum:]][_[:alnum:]]*', {bash: false}), '[_a-zA-Z][_a-zA-Z0-9][_a-zA-Z0-9]*?(?:\\/|$)', []); + assert.equal(create('[_[:alpha:]][_[:alnum:]][_[:alnum:]]*'), '[_a-zA-Z][_a-zA-Z0-9][_a-zA-Z0-9][^/]*?(?:\\/|$)', []); }); }); diff --git a/test/capture.js b/test/capture.js new file mode 100644 index 00000000..a9049acd --- /dev/null +++ b/test/capture.js @@ -0,0 +1,67 @@ +var capture = require('../').capture; +var assert = require('assert'); + +describe('.capture()', function() { + it('should return null if no match', function() { + assert.equal(capture('test/*', 'hi/123'), null); + }); + + it('should return an empty array if there are no captures', function() { + assert.deepEqual(capture('test/hi', 'test/hi'), []); + }); + + it('should capture stars', function() { + assert.deepEqual(capture('test/*', 'test/foo'), ['foo']); + assert.deepEqual(capture('test/*/bar', 'test/foo/bar'), ['foo']); + assert.deepEqual(capture('test/*/bar/*', 'test/foo/bar/baz'), ['foo', 'baz']); + assert.deepEqual(capture('test/*.js', 'test/foo.js'), ['foo']); + assert.deepEqual(capture('test/*-controller.js', 'test/foo-controller.js'), ['foo']); + }); + + it('should capture globstars', function() { + assert.deepEqual(capture('test/**/*.js', 'test/a.js'), ['', 'a']); + assert.deepEqual(capture('test/**/*.js', 'test/dir/a.js'), ['dir', 'a']); + assert.deepEqual(capture('test/**/*.js', 'test/dir/test/a.js'), ['dir/test', 'a']); + }); + + it('should capture extglobs', function() { + assert.deepEqual(capture('test/+(a|b)/*.js', 'test/a/x.js'), ['a', 'x']); + assert.deepEqual(capture('test/+(a|b)/*.js', 'test/b/x.js'), ['b', 'x']); + assert.deepEqual(capture('test/+(a|b)/*.js', 'test/ab/x.js'), ['ab', 'x']); + }); + + it('should capture paren groups', function() { + assert.deepEqual(capture('test/(a|b)/x.js', 'test/a/x.js'), ['a']); + assert.deepEqual(capture('test/(a|b)/x.js', 'test/b/x.js'), ['b']); + }); + + it('should capture star groups', function() { + assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/a/x.js'), ['']); + assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/aa/x.js'), ['a']); + assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/ab/x.js'), ['b']); + assert.deepEqual(capture('test/a*(a|b)/x.js', 'test/aba/x.js'), ['ba']); + }); + + it('should capture plus groups', function() { + assert.deepEqual(capture('test/+(a|b)/x.js', 'test/a/x.js'), ['a']); + assert.deepEqual(capture('test/+(a|b)/x.js', 'test/b/x.js'), ['b']); + assert.deepEqual(capture('test/+(a|b)/x.js', 'test/ab/x.js'), ['ab']); + assert.deepEqual(capture('test/+(a|b)/x.js', 'test/aba/x.js'), ['aba']); + }); + + it('should capture optional groups', function() { + assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/a/x.js'), ['']); + assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/ab/x.js'), ['b']); + assert.deepEqual(capture('test/a?(a|b)/x.js', 'test/aa/x.js'), ['a']); + }); + + it('should capture @ groups', function() { + assert.deepEqual(capture('test/@(a|b)/x.js', 'test/a/x.js'), ['a']); + assert.deepEqual(capture('test/@(a|b)/x.js', 'test/b/x.js'), ['b']); + }); + + it('should capture negated groups', function() { + assert.deepEqual(capture('test/!(a|b)/x.js', 'test/x/x.js'), ['x']); + assert.deepEqual(capture('test/!(a|b)/x.js', 'test/y/x.js'), ['y']); + }); +});