1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 | function globToRegExp(glob) {
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | if(/^\(.+\)$/.test(glob)) {
|
21 |
|
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;
|