You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
4.1 KiB
202 lines
4.1 KiB
4 years ago
|
/*
|
||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||
|
Author Tobias Koppers @sokra
|
||
|
*/
|
||
|
"use strict";
|
||
|
|
||
|
function globToRegExp(glob) {
|
||
|
// * [^\\\/]*
|
||
|
// /**/ /.+/
|
||
|
// ^* \./.+ (concord special)
|
||
|
// ? [^\\\/]
|
||
|
// [!...] [^...]
|
||
|
// [^...] [^...]
|
||
|
// / [\\\/]
|
||
|
// {...,...} (...|...)
|
||
|
// ?(...|...) (...|...)?
|
||
|
// +(...|...) (...|...)+
|
||
|
// *(...|...) (...|...)*
|
||
|
// @(...|...) (...|...)
|
||
|
if (/^\(.+\)$/.test(glob)) {
|
||
|
// allow to pass an RegExp in brackets
|
||
|
return new RegExp(glob.substr(1, glob.length - 2));
|
||
|
}
|
||
|
const tokens = tokenize(glob);
|
||
|
const process = createRoot();
|
||
|
const regExpStr = tokens.map(process).join("");
|
||
|
return new RegExp("^" + regExpStr + "$");
|
||
|
}
|
||
|
|
||
|
const SIMPLE_TOKENS = {
|
||
|
"@(": "one",
|
||
|
"?(": "zero-one",
|
||
|
"+(": "one-many",
|
||
|
"*(": "zero-many",
|
||
|
"|": "segment-sep",
|
||
|
"/**/": "any-path-segments",
|
||
|
"**": "any-path",
|
||
|
"*": "any-path-segment",
|
||
|
"?": "any-char",
|
||
|
"{": "or",
|
||
|
"/": "path-sep",
|
||
|
",": "comma",
|
||
|
")": "closing-segment",
|
||
|
"}": "closing-or"
|
||
|
};
|
||
|
|
||
|
function tokenize(glob) {
|
||
|
return glob
|
||
|
.split(
|
||
|
/([@?+*]\(|\/\*\*\/|\*\*|[?*]|\[[!^]?(?:[^\]\\]|\\.)+\]|\{|,|\/|[|)}])/g
|
||
|
)
|
||
|
.map(item => {
|
||
|
if (!item) return null;
|
||
|
const t = SIMPLE_TOKENS[item];
|
||
|
if (t) {
|
||
|
return {
|
||
|
type: t
|
||
|
};
|
||
|
}
|
||
|
if (item[0] === "[") {
|
||
|
if (item[1] === "^" || item[1] === "!") {
|
||
|
return {
|
||
|
type: "inverted-char-set",
|
||
|
value: item.substr(2, item.length - 3)
|
||
|
};
|
||
|
} else {
|
||
|
return {
|
||
|
type: "char-set",
|
||
|
value: item.substr(1, item.length - 2)
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
return {
|
||
|
type: "string",
|
||
|
value: item
|
||
|
};
|
||
|
})
|
||
|
.filter(Boolean)
|
||
|
.concat({
|
||
|
type: "end"
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function createRoot() {
|
||
|
const inOr = [];
|
||
|
const process = createSeqment();
|
||
|
let initial = true;
|
||
|
return function(token) {
|
||
|
switch (token.type) {
|
||
|
case "or":
|
||
|
inOr.push(initial);
|
||
|
return "(";
|
||
|
case "comma":
|
||
|
if (inOr.length) {
|
||
|
initial = inOr[inOr.length - 1];
|
||
|
return "|";
|
||
|
} else {
|
||
|
return process(
|
||
|
{
|
||
|
type: "string",
|
||
|
value: ","
|
||
|
},
|
||
|
initial
|
||
|
);
|
||
|
}
|
||
|
case "closing-or":
|
||
|
if (inOr.length === 0) throw new Error("Unmatched '}'");
|
||
|
inOr.pop();
|
||
|
return ")";
|
||
|
case "end":
|
||
|
if (inOr.length) throw new Error("Unmatched '{'");
|
||
|
return process(token, initial);
|
||
|
default: {
|
||
|
const result = process(token, initial);
|
||
|
initial = false;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function createSeqment() {
|
||
|
const inSeqment = [];
|
||
|
const process = createSimple();
|
||
|
return function(token, initial) {
|
||
|
switch (token.type) {
|
||
|
case "one":
|
||
|
case "one-many":
|
||
|
case "zero-many":
|
||
|
case "zero-one":
|
||
|
inSeqment.push(token.type);
|
||
|
return "(";
|
||
|
case "segment-sep":
|
||
|
if (inSeqment.length) {
|
||
|
return "|";
|
||
|
} else {
|
||
|
return process(
|
||
|
{
|
||
|
type: "string",
|
||
|
value: "|"
|
||
|
},
|
||
|
initial
|
||
|
);
|
||
|
}
|
||
|
case "closing-segment": {
|
||
|
const segment = inSeqment.pop();
|
||
|
switch (segment) {
|
||
|
case "one":
|
||
|
return ")";
|
||
|
case "one-many":
|
||
|
return ")+";
|
||
|
case "zero-many":
|
||
|
return ")*";
|
||
|
case "zero-one":
|
||
|
return ")?";
|
||
|
}
|
||
|
throw new Error("Unexcepted segment " + segment);
|
||
|
}
|
||
|
case "end":
|
||
|
if (inSeqment.length > 0) {
|
||
|
throw new Error("Unmatched segment, missing ')'");
|
||
|
}
|
||
|
return process(token, initial);
|
||
|
default:
|
||
|
return process(token, initial);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function createSimple() {
|
||
|
return function(token, initial) {
|
||
|
switch (token.type) {
|
||
|
case "path-sep":
|
||
|
return "[\\\\/]+";
|
||
|
case "any-path-segments":
|
||
|
return "[\\\\/]+(?:(.+)[\\\\/]+)?";
|
||
|
case "any-path":
|
||
|
return "(.*)";
|
||
|
case "any-path-segment":
|
||
|
if (initial) {
|
||
|
return "\\.[\\\\/]+(?:.*[\\\\/]+)?([^\\\\/]+)";
|
||
|
} else {
|
||
|
return "([^\\\\/]*)";
|
||
|
}
|
||
|
case "any-char":
|
||
|
return "[^\\\\/]";
|
||
|
case "inverted-char-set":
|
||
|
return "[^" + token.value + "]";
|
||
|
case "char-set":
|
||
|
return "[" + token.value + "]";
|
||
|
case "string":
|
||
|
return token.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||
|
case "end":
|
||
|
return "";
|
||
|
default:
|
||
|
throw new Error("Unsupported token '" + token.type + "'");
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
exports.globToRegExp = globToRegExp;
|