diff options
| author | Nayeem Rahman <nayeemrmn99@gmail.com> | 2020-08-06 13:46:31 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-08-06 08:46:31 -0400 |
| commit | 24590b012f72ba1a02a5b90ef6c1156606a6ea53 (patch) | |
| tree | 515f09d4e39213eed07720843e3b4ef39619d3b3 /std/path/glob.ts | |
| parent | f6cd36f8c84ed373930431c325d9c23b78600b74 (diff) | |
refactor: Rewrite globToRegExp() (#6963)
Diffstat (limited to 'std/path/glob.ts')
| -rw-r--r-- | std/path/glob.ts | 274 |
1 files changed, 236 insertions, 38 deletions
diff --git a/std/path/glob.ts b/std/path/glob.ts index 172587626..2fff76f34 100644 --- a/std/path/glob.ts +++ b/std/path/glob.ts @@ -1,56 +1,254 @@ +// globToRegExp() is originall ported from globrex@0.1.2. +// Copyright 2018 Terkel Gjervig Nielsen. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -/** This module is browser compatible. */ +// This module is browser compatible. -import { SEP, SEP_PATTERN } from "./separator.ts"; -import { globrex } from "./_globrex.ts"; +import { NATIVE_OS } from "./_constants.ts"; import { join, normalize } from "./mod.ts"; -import { assert } from "../_util/assert.ts"; +import { SEP, SEP_PATTERN } from "./separator.ts"; export interface GlobOptions { + /** Extended glob syntax. + * See https://www.linuxjournal.com/content/bash-extended-globbing. Defaults + * to true. */ extended?: boolean; + /** Globstar syntax. + * See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. + * If false, `**` is treated like `*`. Defaults to true. */ globstar?: boolean; + /** Operating system. Defaults to the native OS. */ + os?: typeof Deno.build.os; } -export interface GlobToRegExpOptions extends GlobOptions { - flags?: string; -} +export type GlobToRegExpOptions = GlobOptions; -/** - * Generate a regex based on glob pattern and options - * This was meant to be using the the `fs.walk` function - * but can be used anywhere else. - * Examples: - * - * Looking for all the `ts` files: - * walkSync(".", { - * match: [globToRegExp("*.ts")] - * }) +/** Convert a glob string to a regular expressions. * - * Looking for all the `.json` files in any subfolder: - * walkSync(".", { - * match: [globToRegExp(join("a", "**", "*.json"),{ - * flags: "g", - * extended: true, - * globstar: true - * })] - * }) + * // Looking for all the `ts` files: + * walkSync(".", { + * match: [globToRegExp("*.ts")] + * }); * - * @param glob - Glob pattern to be used - * @param options - Specific options for the glob pattern - * @returns A RegExp for the glob pattern - */ + * Looking for all the `.json` files in any subfolder: + * walkSync(".", { + * match: [globToRegExp(join("a", "**", "*.json"), { + * extended: true, + * globstar: true + * })] + * }); */ export function globToRegExp( glob: string, - { extended = false, globstar = true }: GlobToRegExpOptions = {}, + { extended = true, globstar: globstarOption = true, os = NATIVE_OS }: + GlobToRegExpOptions = {}, ): RegExp { - const result = globrex(glob, { - extended, - globstar, - strict: false, - filepath: true, - }); - assert(result.path != null); - return result.path.regex; + const sep = os == "windows" ? `(?:\\\\|\\/)+` : `\\/+`; + const sepMaybe = os == "windows" ? `(?:\\\\|\\/)*` : `\\/*`; + const seps = os == "windows" ? ["\\", "/"] : ["/"]; + const sepRaw = os == "windows" ? `\\` : `/`; + const globstar = os == "windows" + ? `(?:[^\\\\/]*(?:\\\\|\\/|$)+)*` + : `(?:[^/]*(?:\\/|$)+)*`; + const wildcard = os == "windows" ? `[^\\\\/]*` : `[^/]*`; + + // Keep track of scope for extended syntaxes. + const extStack = []; + + // 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; + + let regExpString = ""; + + // Remove trailing separators. + let newLength = glob.length; + for (; newLength > 0 && seps.includes(glob[newLength - 1]); newLength--); + glob = glob.slice(0, newLength); + + let c, n; + for (let i = 0; i < glob.length; i++) { + c = glob[i]; + n = glob[i + 1]; + + if (seps.includes(c)) { + regExpString += sep; + while (seps.includes(glob[i + 1])) i++; + continue; + } + + if (c == "[") { + if (inRange && n == ":") { + i++; // skip [ + let value = ""; + while (glob[++i] !== ":") value += glob[i]; + if (value == "alnum") regExpString += "\\w\\d"; + else if (value == "space") regExpString += "\\s"; + else if (value == "digit") regExpString += "\\d"; + i++; // skip last ] + continue; + } + inRange = true; + regExpString += c; + continue; + } + + if (c == "]") { + inRange = false; + regExpString += c; + continue; + } + + if (c == "!") { + if (inRange) { + if (glob[i - 1] == "[") { + regExpString += "^"; + continue; + } + } else if (extended) { + if (n == "(") { + extStack.push(c); + regExpString += "(?!"; + i++; + continue; + } + regExpString += `\\${c}`; + continue; + } else { + regExpString += `\\${c}`; + continue; + } + } + + if (inRange) { + if (c == "\\" || c == "^" && glob[i - 1] == "[") regExpString += `\\${c}`; + else regExpString += c; + continue; + } + + if (["\\", "$", "^", ".", "="].includes(c)) { + regExpString += `\\${c}`; + continue; + } + + if (c == "(") { + if (extStack.length) { + regExpString += `${c}?:`; + continue; + } + regExpString += `\\${c}`; + continue; + } + + if (c == ")") { + if (extStack.length) { + regExpString += c; + const type = extStack.pop()!; + if (type == "@") { + regExpString += "{1}"; + } else if (type == "!") { + regExpString += wildcard; + } else { + regExpString += type; + } + continue; + } + regExpString += `\\${c}`; + continue; + } + + if (c == "|") { + if (extStack.length) { + regExpString += c; + continue; + } + regExpString += `\\${c}`; + continue; + } + + if (c == "+") { + if (n == "(" && extended) { + extStack.push(c); + continue; + } + regExpString += `\\${c}`; + continue; + } + + if (c == "@" && extended) { + if (n == "(") { + extStack.push(c); + continue; + } + } + + if (c == "?") { + if (extended) { + if (n == "(") { + extStack.push(c); + } + continue; + } else { + regExpString += "."; + continue; + } + } + + if (c == "{") { + inGroup = true; + regExpString += "(?:"; + continue; + } + + if (c == "}") { + inGroup = false; + regExpString += ")"; + continue; + } + + if (c == ",") { + if (inGroup) { + regExpString += "|"; + continue; + } + regExpString += `\\${c}`; + continue; + } + + if (c == "*") { + if (n == "(" && extended) { + extStack.push(c); + continue; + } + // Move over all consecutive "*"'s. + // Also store the previous and next characters + const prevChar = glob[i - 1]; + let starCount = 1; + while (glob[i + 1] == "*") { + starCount++; + i++; + } + const nextChar = glob[i + 1]; + const isGlobstar = globstarOption && starCount > 1 && + // from the start of the segment + [sepRaw, "/", undefined].includes(prevChar) && + // to the end of the segment + [sepRaw, "/", undefined].includes(nextChar); + if (isGlobstar) { + // it's a globstar, so match zero or more path segments + regExpString += globstar; + while (seps.includes(glob[i + 1])) i++; + } else { + // it's not a globstar, so only match one path segment + regExpString += wildcard; + } + continue; + } + + regExpString += c; + } + + regExpString = `^${regExpString}${regExpString != "" ? sepMaybe : ""}$`; + return new RegExp(regExpString); } /** Test whether the given string is a glob */ |
