summaryrefslogtreecommitdiff
path: root/std/path/glob.ts
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2020-08-06 13:46:31 +0100
committerGitHub <noreply@github.com>2020-08-06 08:46:31 -0400
commit24590b012f72ba1a02a5b90ef6c1156606a6ea53 (patch)
tree515f09d4e39213eed07720843e3b4ef39619d3b3 /std/path/glob.ts
parentf6cd36f8c84ed373930431c325d9c23b78600b74 (diff)
refactor: Rewrite globToRegExp() (#6963)
Diffstat (limited to 'std/path/glob.ts')
-rw-r--r--std/path/glob.ts274
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 */