1
|
/*
|
2
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
3
|
Author Tobias Koppers @sokra
|
4
|
*/
|
5
|
"use strict";
|
6
|
|
7
|
function globToRegExp(glob) {
|
8
|
// * [^\\\/]*
|
9
|
// /**/ /.+/
|
10
|
// ^* \./.+ (concord special)
|
11
|
// ? [^\\\/]
|
12
|
// [!...] [^...]
|
13
|
// [^...] [^...]
|
14
|
// / [\\\/]
|
15
|
// {...,...} (...|...)
|
16
|
// ?(...|...) (...|...)?
|
17
|
// +(...|...) (...|...)+
|
18
|
// *(...|...) (...|...)*
|
19
|
// @(...|...) (...|...)
|
20
|
if(/^\(.+\)$/.test(glob)) {
|
21
|
// allow to pass an RegExp in brackets
|
22
|
return new RegExp(glob.substr(1, glob.length - 2));
|
23
|
}
|
24
|
const tokens = tokenize(glob);
|
25
|
const process = createRoot();
|
26
|
const regExpStr = tokens.map(process).join("");
|
27
|
return new RegExp("^" + regExpStr + "$");
|
28
|
}
|
29
|
|
30
|
const SIMPLE_TOKENS = {
|
31
|
"@(": "one",
|
32
|
"?(": "zero-one",
|
33
|
"+(": "one-many",
|
34
|
"*(": "zero-many",
|
35
|
"|": "segment-sep",
|
36
|
"/**/": "any-path-segments",
|
37
|
"**": "any-path",
|
38
|
"*": "any-path-segment",
|
39
|
"?": "any-char",
|
40
|
"{": "or",
|
41
|
"/": "path-sep",
|
42
|
",": "comma",
|
43
|
")": "closing-segment",
|
44
|
"}": "closing-or"
|
45
|
};
|
46
|
|
47
|
function tokenize(glob) {
|
48
|
return glob.split(/([@?+*]\(|\/\*\*\/|\*\*|[?*]|\[[\!\^]?(?:[^\]\\]|\\.)+\]|\{|,|\/|[|)}])/g).map(item => {
|
49
|
if(!item)
|
50
|
return null;
|
51
|
const t = SIMPLE_TOKENS[item];
|
52
|
if(t) {
|
53
|
return {
|
54
|
type: t
|
55
|
};
|
56
|
}
|
57
|
if(item[0] === "[") {
|
58
|
if(item[1] === "^" || item[1] === "!") {
|
59
|
return {
|
60
|
type: "inverted-char-set",
|
61
|
value: item.substr(2, item.length - 3)
|
62
|
};
|
63
|
} else {
|
64
|
return {
|
65
|
type: "char-set",
|
66
|
value: item.substr(1, item.length - 2)
|
67
|
};
|
68
|
}
|
69
|
}
|
70
|
return {
|
71
|
type: "string",
|
72
|
value: item
|
73
|
};
|
74
|
}).filter(Boolean).concat({
|
75
|
type: "end"
|
76
|
});
|
77
|
}
|
78
|
|
79
|
function createRoot() {
|
80
|
const inOr = [];
|
81
|
const process = createSeqment();
|
82
|
let initial = true;
|
83
|
return function(token) {
|
84
|
switch(token.type) {
|
85
|
case "or":
|
86
|
inOr.push(initial);
|
87
|
return "(";
|
88
|
case "comma":
|
89
|
if(inOr.length) {
|
90
|
initial = inOr[inOr.length - 1];
|
91
|
return "|";
|
92
|
} else {
|
93
|
return process({
|
94
|
type: "string",
|
95
|
value: ","
|
96
|
}, initial);
|
97
|
}
|
98
|
case "closing-or":
|
99
|
if(inOr.length === 0)
|
100
|
throw new Error("Unmatched '}'");
|
101
|
inOr.pop();
|
102
|
return ")";
|
103
|
case "end":
|
104
|
if(inOr.length)
|
105
|
throw new Error("Unmatched '{'");
|
106
|
return process(token, initial);
|
107
|
default:
|
108
|
{
|
109
|
const result = process(token, initial);
|
110
|
initial = false;
|
111
|
return result;
|
112
|
}
|
113
|
}
|
114
|
};
|
115
|
}
|
116
|
|
117
|
function createSeqment() {
|
118
|
const inSeqment = [];
|
119
|
const process = createSimple();
|
120
|
return function(token, initial) {
|
121
|
switch(token.type) {
|
122
|
case "one":
|
123
|
case "one-many":
|
124
|
case "zero-many":
|
125
|
case "zero-one":
|
126
|
inSeqment.push(token.type);
|
127
|
return "(";
|
128
|
case "segment-sep":
|
129
|
if(inSeqment.length) {
|
130
|
return "|";
|
131
|
} else {
|
132
|
return process({
|
133
|
type: "string",
|
134
|
value: "|"
|
135
|
}, initial);
|
136
|
}
|
137
|
case "closing-segment":
|
138
|
{
|
139
|
const segment = inSeqment.pop();
|
140
|
switch(segment) {
|
141
|
case "one":
|
142
|
return ")";
|
143
|
case "one-many":
|
144
|
return ")+";
|
145
|
case "zero-many":
|
146
|
return ")*";
|
147
|
case "zero-one":
|
148
|
return ")?";
|
149
|
}
|
150
|
throw new Error("Unexcepted segment " + segment);
|
151
|
}
|
152
|
case "end":
|
153
|
if(inSeqment.length > 0) {
|
154
|
throw new Error("Unmatched segment, missing ')'");
|
155
|
}
|
156
|
return process(token, initial);
|
157
|
default:
|
158
|
return process(token, initial);
|
159
|
}
|
160
|
};
|
161
|
}
|
162
|
|
163
|
function createSimple() {
|
164
|
return function(token, initial) {
|
165
|
switch(token.type) {
|
166
|
case "path-sep":
|
167
|
return "[\\\\/]+";
|
168
|
case "any-path-segments":
|
169
|
return "[\\\\/]+(?:(.+)[\\\\/]+)?";
|
170
|
case "any-path":
|
171
|
return "(.*)";
|
172
|
case "any-path-segment":
|
173
|
if(initial) {
|
174
|
return "\\.[\\\\/]+(?:.*[\\\\/]+)?([^\\\\/]+)";
|
175
|
} else {
|
176
|
return "([^\\\\/]*)";
|
177
|
}
|
178
|
case "any-char":
|
179
|
return "[^\\\\/]";
|
180
|
case "inverted-char-set":
|
181
|
return "[^" + token.value + "]";
|
182
|
case "char-set":
|
183
|
return "[" + token.value + "]";
|
184
|
case "string":
|
185
|
return token.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
186
|
case "end":
|
187
|
return "";
|
188
|
default:
|
189
|
throw new Error("Unsupported token '" + token.type + "'");
|
190
|
}
|
191
|
};
|
192
|
}
|
193
|
|
194
|
exports.globToRegExp = globToRegExp;
|