1 | module.exports = function (glob, opts) {
|
---|
2 | if (typeof glob !== 'string') {
|
---|
3 | throw new TypeError('Expected a string');
|
---|
4 | }
|
---|
5 |
|
---|
6 | var str = String(glob);
|
---|
7 |
|
---|
8 | // The regexp we are building, as a string.
|
---|
9 | var reStr = "";
|
---|
10 |
|
---|
11 | // Whether we are matching so called "extended" globs (like bash) and should
|
---|
12 | // support single character matching, matching ranges of characters, group
|
---|
13 | // matching, etc.
|
---|
14 | var extended = opts ? !!opts.extended : false;
|
---|
15 |
|
---|
16 | // When globstar is _false_ (default), '/foo/*' is translated a regexp like
|
---|
17 | // '^\/foo\/.*$' which will match any string beginning with '/foo/'
|
---|
18 | // When globstar is _true_, '/foo/*' is translated to regexp like
|
---|
19 | // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT
|
---|
20 | // which does not have a '/' to the right of it.
|
---|
21 | // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but
|
---|
22 | // these will not '/foo/bar/baz', '/foo/bar/baz.txt'
|
---|
23 | // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when
|
---|
24 | // globstar is _false_
|
---|
25 | var globstar = opts ? !!opts.globstar : false;
|
---|
26 |
|
---|
27 | // If we are doing extended matching, this boolean is true when we are inside
|
---|
28 | // a group (eg {*.html,*.js}), and false otherwise.
|
---|
29 | var inGroup = false;
|
---|
30 |
|
---|
31 | // RegExp flags (eg "i" ) to pass in to RegExp constructor.
|
---|
32 | var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : "";
|
---|
33 |
|
---|
34 | var c;
|
---|
35 | for (var i = 0, len = str.length; i < len; i++) {
|
---|
36 | c = str[i];
|
---|
37 |
|
---|
38 | switch (c) {
|
---|
39 | case "/":
|
---|
40 | case "$":
|
---|
41 | case "^":
|
---|
42 | case "+":
|
---|
43 | case ".":
|
---|
44 | case "(":
|
---|
45 | case ")":
|
---|
46 | case "=":
|
---|
47 | case "!":
|
---|
48 | case "|":
|
---|
49 | reStr += "\\" + c;
|
---|
50 | break;
|
---|
51 |
|
---|
52 | case "?":
|
---|
53 | if (extended) {
|
---|
54 | reStr += ".";
|
---|
55 | break;
|
---|
56 | }
|
---|
57 |
|
---|
58 | case "[":
|
---|
59 | case "]":
|
---|
60 | if (extended) {
|
---|
61 | reStr += c;
|
---|
62 | break;
|
---|
63 | }
|
---|
64 |
|
---|
65 | case "{":
|
---|
66 | if (extended) {
|
---|
67 | inGroup = true;
|
---|
68 | reStr += "(";
|
---|
69 | break;
|
---|
70 | }
|
---|
71 |
|
---|
72 | case "}":
|
---|
73 | if (extended) {
|
---|
74 | inGroup = false;
|
---|
75 | reStr += ")";
|
---|
76 | break;
|
---|
77 | }
|
---|
78 |
|
---|
79 | case ",":
|
---|
80 | if (inGroup) {
|
---|
81 | reStr += "|";
|
---|
82 | break;
|
---|
83 | }
|
---|
84 | reStr += "\\" + c;
|
---|
85 | break;
|
---|
86 |
|
---|
87 | case "*":
|
---|
88 | // Move over all consecutive "*"'s.
|
---|
89 | // Also store the previous and next characters
|
---|
90 | var prevChar = str[i - 1];
|
---|
91 | var starCount = 1;
|
---|
92 | while(str[i + 1] === "*") {
|
---|
93 | starCount++;
|
---|
94 | i++;
|
---|
95 | }
|
---|
96 | var nextChar = str[i + 1];
|
---|
97 |
|
---|
98 | if (!globstar) {
|
---|
99 | // globstar is disabled, so treat any number of "*" as one
|
---|
100 | reStr += ".*";
|
---|
101 | } else {
|
---|
102 | // globstar is enabled, so determine if this is a globstar segment
|
---|
103 | var isGlobstar = starCount > 1 // multiple "*"'s
|
---|
104 | && (prevChar === "/" || prevChar === undefined) // from the start of the segment
|
---|
105 | && (nextChar === "/" || nextChar === undefined) // to the end of the segment
|
---|
106 |
|
---|
107 | if (isGlobstar) {
|
---|
108 | // it's a globstar, so match zero or more path segments
|
---|
109 | reStr += "((?:[^/]*(?:\/|$))*)";
|
---|
110 | i++; // move over the "/"
|
---|
111 | } else {
|
---|
112 | // it's not a globstar, so only match one path segment
|
---|
113 | reStr += "([^/]*)";
|
---|
114 | }
|
---|
115 | }
|
---|
116 | break;
|
---|
117 |
|
---|
118 | default:
|
---|
119 | reStr += c;
|
---|
120 | }
|
---|
121 | }
|
---|
122 |
|
---|
123 | // When regexp 'g' flag is specified don't
|
---|
124 | // constrain the regular expression with ^ & $
|
---|
125 | if (!flags || !~flags.indexOf('g')) {
|
---|
126 | reStr = "^" + reStr + "$";
|
---|
127 | }
|
---|
128 |
|
---|
129 | return new RegExp(reStr, flags);
|
---|
130 | };
|
---|