summaryrefslogtreecommitdiff
path: root/fs/globrex.ts
diff options
context:
space:
mode:
Diffstat (limited to 'fs/globrex.ts')
-rw-r--r--fs/globrex.ts315
1 files changed, 315 insertions, 0 deletions
diff --git a/fs/globrex.ts b/fs/globrex.ts
new file mode 100644
index 000000000..06a6b79bf
--- /dev/null
+++ b/fs/globrex.ts
@@ -0,0 +1,315 @@
+// This file is ported from globrex@0.1.2
+// MIT License
+// Copyright (c) 2018 Terkel Gjervig Nielsen
+
+import * as deno from "deno";
+
+const isWin = deno.platform.os === "win";
+const SEP = isWin ? `\\\\+` : `\\/`;
+const SEP_ESC = isWin ? `\\\\` : `/`;
+const GLOBSTAR = `((?:[^/]*(?:/|$))*)`;
+const WILDCARD = `([^/]*)`;
+const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`;
+const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`;
+
+export interface GlobOptions {
+ extended?: boolean;
+ globstar?: boolean;
+ strict?: boolean;
+ filepath?: boolean;
+ flags?: string;
+}
+
+/**
+ * Convert any glob pattern to a JavaScript Regexp object
+ * @param {String} glob Glob pattern to convert
+ * @param {Object} opts Configuration object
+ * @param {Boolean} [opts.extended=false] Support advanced ext globbing
+ * @param {Boolean} [opts.globstar=false] Support globstar
+ * @param {Boolean} [opts.strict=true] be laissez faire about mutiple slashes
+ * @param {Boolean} [opts.filepath=''] Parse as filepath for extra path related features
+ * @param {String} [opts.flags=''] RegExp globs
+ * @returns {Object} converted object with string, segments and RegExp object
+ */
+export function globrex(
+ glob: string,
+ {
+ extended = false,
+ globstar = false,
+ strict = false,
+ filepath = false,
+ flags = ""
+ }: GlobOptions = {}
+) {
+ let regex = "";
+ let segment = "";
+ let path: {
+ regex: string | RegExp;
+ segments: Array<RegExp>;
+ globstar?: RegExp;
+ } = { regex: "", segments: [] };
+
+ // If we are doing extended matching, this boolean is true when we are inside
+ // a group (eg {*.html,*.js}), and false otherwise.
+ let inGroup = false;
+ let inRange = false;
+
+ // extglob stack. Keep track of scope
+ const ext = [];
+
+ interface AddOptions {
+ split?: boolean;
+ last?: boolean;
+ only?: string;
+ }
+
+ // Helper function to build string and segments
+ function add(
+ str,
+ options: AddOptions = { split: false, last: false, only: "" }
+ ) {
+ const { split, last, only } = options;
+ if (only !== "path") regex += str;
+ if (filepath && only !== "regex") {
+ path.regex += str === "\\/" ? SEP : str;
+ if (split) {
+ if (last) segment += str;
+ if (segment !== "") {
+ if (!flags.includes("g")) segment = `^${segment}$`; // change it 'includes'
+ path.segments.push(new RegExp(segment, flags));
+ }
+ segment = "";
+ } else {
+ segment += str;
+ }
+ }
+ }
+
+ let c, n;
+ for (let i = 0; i < glob.length; i++) {
+ c = glob[i];
+ n = glob[i + 1];
+
+ if (["\\", "$", "^", ".", "="].includes(c)) {
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "/") {
+ add(`\\${c}`, { split: true });
+ if (n === "/" && !strict) regex += "?";
+ continue;
+ }
+
+ if (c === "(") {
+ if (ext.length) {
+ add(c);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === ")") {
+ if (ext.length) {
+ add(c);
+ let type = ext.pop();
+ if (type === "@") {
+ add("{1}");
+ } else if (type === "!") {
+ add("([^/]*)");
+ } else {
+ add(type);
+ }
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "|") {
+ if (ext.length) {
+ add(c);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "+") {
+ if (n === "(" && extended) {
+ ext.push(c);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "@" && extended) {
+ if (n === "(") {
+ ext.push(c);
+ continue;
+ }
+ }
+
+ if (c === "!") {
+ if (extended) {
+ if (inRange) {
+ add("^");
+ continue;
+ }
+ if (n === "(") {
+ ext.push(c);
+ add("(?!");
+ i++;
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "?") {
+ if (extended) {
+ if (n === "(") {
+ ext.push(c);
+ } else {
+ add(".");
+ }
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "[") {
+ if (inRange && n === ":") {
+ i++; // skip [
+ let value = "";
+ while (glob[++i] !== ":") value += glob[i];
+ if (value === "alnum") add("(\\w|\\d)");
+ else if (value === "space") add("\\s");
+ else if (value === "digit") add("\\d");
+ i++; // skip last ]
+ continue;
+ }
+ if (extended) {
+ inRange = true;
+ add(c);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "]") {
+ if (extended) {
+ inRange = false;
+ add(c);
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "{") {
+ if (extended) {
+ inGroup = true;
+ add("(");
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "}") {
+ if (extended) {
+ inGroup = false;
+ add(")");
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === ",") {
+ if (inGroup) {
+ add("|");
+ continue;
+ }
+ add(`\\${c}`);
+ continue;
+ }
+
+ if (c === "*") {
+ if (n === "(" && extended) {
+ ext.push(c);
+ continue;
+ }
+ // Move over all consecutive "*"'s.
+ // Also store the previous and next characters
+ let prevChar = glob[i - 1];
+ let starCount = 1;
+ while (glob[i + 1] === "*") {
+ starCount++;
+ i++;
+ }
+ let nextChar = glob[i + 1];
+ if (!globstar) {
+ // globstar is disabled, so treat any number of "*" as one
+ add(".*");
+ } else {
+ // globstar is enabled, so determine if this is a globstar segment
+ let isGlobstar =
+ starCount > 1 && // multiple "*"'s
+ (prevChar === "/" || prevChar === undefined) && // from the start of the segment
+ (nextChar === "/" || nextChar === undefined); // to the end of the segment
+ if (isGlobstar) {
+ // it's a globstar, so match zero or more path segments
+ add(GLOBSTAR, { only: "regex" });
+ add(GLOBSTAR_SEGMENT, { only: "path", last: true, split: true });
+ i++; // move over the "/"
+ } else {
+ // it's not a globstar, so only match one path segment
+ add(WILDCARD, { only: "regex" });
+ add(WILDCARD_SEGMENT, { only: "path" });
+ }
+ }
+ continue;
+ }
+
+ add(c);
+ }
+
+ // When regexp 'g' flag is specified don't
+ // constrain the regular expression with ^ & $
+ if (!flags.includes("g")) {
+ regex = `^${regex}$`;
+ segment = `^${segment}$`;
+ if (filepath) path.regex = `^${path.regex}$`;
+ }
+
+ const result: {
+ regex: RegExp;
+ path?: {
+ regex: string | RegExp;
+ segments: Array<RegExp>;
+ globstar?: RegExp;
+ };
+ } = { regex: new RegExp(regex, flags) };
+
+ // Push the last segment
+ if (filepath) {
+ path.segments.push(new RegExp(segment, flags));
+ path.regex = new RegExp(path.regex.toString(), flags);
+ path.globstar = new RegExp(
+ !flags.includes("g") ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT,
+ flags
+ );
+ result.path = path;
+ }
+
+ return result;
+}