summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/glob.ts32
-rw-r--r--fs/glob_test.ts119
2 files changed, 149 insertions, 2 deletions
diff --git a/fs/glob.ts b/fs/glob.ts
index e7e5aee21..8d322b761 100644
--- a/fs/glob.ts
+++ b/fs/glob.ts
@@ -43,3 +43,35 @@ export interface GlobOptions {
export function glob(glob: string, options: GlobOptions = {}): RegExp {
return globrex(glob, options).regex;
}
+
+/** Test whether the given string is a glob */
+export function isGlob(str: string): boolean {
+ const chars: Record<string, string> = { "{": "}", "(": ")", "[": "]" };
+ const regex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
+
+ if (str === "") {
+ return false;
+ }
+
+ let match: RegExpExecArray | null;
+
+ while ((match = regex.exec(str))) {
+ if (match[2]) return true;
+ let idx = match.index + match[0].length;
+
+ // if an open bracket/brace/paren is escaped,
+ // set the index to the next closing character
+ const open = match[1];
+ const close = open ? chars[open] : null;
+ if (open && close) {
+ const n = str.indexOf(close, idx);
+ if (n !== -1) {
+ idx = n + 1;
+ }
+ }
+
+ str = str.slice(idx);
+ }
+
+ return false;
+}
diff --git a/fs/glob_test.ts b/fs/glob_test.ts
index 3139cb7f4..9151b9e9e 100644
--- a/fs/glob_test.ts
+++ b/fs/glob_test.ts
@@ -1,8 +1,8 @@
const { mkdir } = Deno;
type FileInfo = Deno.FileInfo;
import { test, runIfMain } from "../testing/mod.ts";
-import { assertEquals } from "../testing/asserts.ts";
-import { glob } from "./glob.ts";
+import { assert, assertEquals } from "../testing/asserts.ts";
+import { glob, isGlob } from "./glob.ts";
import { join } from "./path.ts";
import { testWalk } from "./walk_test.ts";
import { touch, walkArray } from "./walk_test.ts";
@@ -138,4 +138,119 @@ testWalk(
}
);
+test({
+ name: "isGlob: pattern to test",
+ fn(): void {
+ // should be true if valid glob pattern
+ assert(isGlob("!foo.js"));
+ assert(isGlob("*.js"));
+ assert(isGlob("!*.js"));
+ assert(isGlob("!foo"));
+ assert(isGlob("!foo.js"));
+ assert(isGlob("**/abc.js"));
+ assert(isGlob("abc/*.js"));
+ assert(isGlob("@.(?:abc)"));
+ assert(isGlob("@.(?!abc)"));
+
+ // should be false if invalid glob pattern
+ assert(!isGlob(""));
+ assert(!isGlob("~/abc"));
+ assert(!isGlob("~/abc"));
+ assert(!isGlob("~/(abc)"));
+ assert(!isGlob("+~(abc)"));
+ assert(!isGlob("."));
+ assert(!isGlob("@.(abc)"));
+ assert(!isGlob("aa"));
+ assert(!isGlob("who?"));
+ assert(!isGlob("why!?"));
+ assert(!isGlob("where???"));
+ assert(!isGlob("abc!/def/!ghi.js"));
+ assert(!isGlob("abc.js"));
+ assert(!isGlob("abc/def/!ghi.js"));
+ assert(!isGlob("abc/def/ghi.js"));
+
+ // Should be true if path has regex capture group
+ assert(isGlob("abc/(?!foo).js"));
+ assert(isGlob("abc/(?:foo).js"));
+ assert(isGlob("abc/(?=foo).js"));
+ assert(isGlob("abc/(a|b).js"));
+ assert(isGlob("abc/(a|b|c).js"));
+ assert(isGlob("abc/(foo bar)/*.js"));
+
+ // Should be false if the path has parens but is not a valid capture group
+ assert(!isGlob("abc/(?foo).js"));
+ assert(!isGlob("abc/(a b c).js"));
+ assert(!isGlob("abc/(ab).js"));
+ assert(!isGlob("abc/(abc).js"));
+ assert(!isGlob("abc/(foo bar).js"));
+
+ // should be false if the capture group is imbalanced
+ assert(!isGlob("abc/(?ab.js"));
+ assert(!isGlob("abc/(ab.js"));
+ assert(!isGlob("abc/(a|b.js"));
+ assert(!isGlob("abc/(a|b|c.js"));
+
+ // should be true if the path has a regex character class
+ assert(isGlob("abc/[abc].js"));
+ assert(isGlob("abc/[^abc].js"));
+ assert(isGlob("abc/[1-3].js"));
+
+ // should be false if the character class is not balanced
+ assert(!isGlob("abc/[abc.js"));
+ assert(!isGlob("abc/[^abc.js"));
+ assert(!isGlob("abc/[1-3.js"));
+
+ // should be false if the character class is escaped
+ assert(!isGlob("abc/\\[abc].js"));
+ assert(!isGlob("abc/\\[^abc].js"));
+ assert(!isGlob("abc/\\[1-3].js"));
+
+ // should be true if the path has brace characters
+ assert(isGlob("abc/{a,b}.js"));
+ assert(isGlob("abc/{a..z}.js"));
+ assert(isGlob("abc/{a..z..2}.js"));
+
+ // should be false if (basic) braces are not balanced
+ assert(!isGlob("abc/\\{a,b}.js"));
+ assert(!isGlob("abc/\\{a..z}.js"));
+ assert(!isGlob("abc/\\{a..z..2}.js"));
+
+ // should be true if the path has regex characters
+ assert(isGlob("!&(abc)"));
+ assert(isGlob("!*.js"));
+ assert(isGlob("!foo"));
+ assert(isGlob("!foo.js"));
+ assert(isGlob("**/abc.js"));
+ assert(isGlob("*.js"));
+ assert(isGlob("*z(abc)"));
+ assert(isGlob("[1-10].js"));
+ assert(isGlob("[^abc].js"));
+ assert(isGlob("[a-j]*[^c]b/c"));
+ assert(isGlob("[abc].js"));
+ assert(isGlob("a/b/c/[a-z].js"));
+ assert(isGlob("abc/(aaa|bbb).js"));
+ assert(isGlob("abc/*.js"));
+ assert(isGlob("abc/{a,b}.js"));
+ assert(isGlob("abc/{a..z..2}.js"));
+ assert(isGlob("abc/{a..z}.js"));
+
+ assert(!isGlob("$(abc)"));
+ assert(!isGlob("&(abc)"));
+ assert(!isGlob("Who?.js"));
+ assert(!isGlob("? (abc)"));
+ assert(!isGlob("?.js"));
+ assert(!isGlob("abc/?.js"));
+
+ // should be false if regex characters are escaped
+ assert(!isGlob("\\?.js"));
+ assert(!isGlob("\\[1-10\\].js"));
+ assert(!isGlob("\\[^abc\\].js"));
+ assert(!isGlob("\\[a-j\\]\\*\\[^c\\]b/c"));
+ assert(!isGlob("\\[abc\\].js"));
+ assert(!isGlob("\\a/b/c/\\[a-z\\].js"));
+ assert(!isGlob("abc/\\(aaa|bbb).js"));
+ assert(!isGlob("abc/\\?.js"));
+ }
+});
+
runIfMain(import.meta);