From a6d43a2a5cce22febd4575f5cbe5db0cbb9619d6 Mon Sep 17 00:00:00 2001 From: Shubhadeep Das Date: Wed, 22 May 2019 22:54:28 +0530 Subject: [PATCH 1/3] Added isGlob function --- fs/glob.ts | 33 ++++++++++++++ fs/glob_test.ts | 118 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/fs/glob.ts b/fs/glob.ts index e7e5aee2124e..fad874832d58 100644 --- a/fs/glob.ts +++ b/fs/glob.ts @@ -43,3 +43,36 @@ 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: any = { "{": "}", "(": ")", "[": "]" }; + const strictRegex: RegExp = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; + + if (str === "") { + return false; + } + + const regex = strictRegex; + 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 3139cb7f4694..61c2fbaf28d6 100644 --- a/fs/glob_test.ts +++ b/fs/glob_test.ts @@ -2,7 +2,7 @@ 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 { 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,120 @@ testWalk( } ); +test({ + name: "isGlob: pattern to test", + fn(): void { + // should be true if valid glob pattern + assertEquals(isGlob("!foo.js"), true); + assertEquals(isGlob("*.js"), true); + assertEquals(isGlob("!*.js"), true); + assertEquals(isGlob("!foo"), true); + assertEquals(isGlob("!foo.js"), true); + assertEquals(isGlob("**/abc.js"), true); + assertEquals(isGlob("abc/*.js"), true); + assertEquals(isGlob("@.(?:abc)"), true); + assertEquals(isGlob("@.(?!abc)"), true); + + // should be false if invalid glob pattern + assertEquals(!isGlob(""), true); + assertEquals(!isGlob(""), true); + assertEquals(!isGlob("~/abc"), true); + assertEquals(!isGlob("~/abc"), true); + assertEquals(!isGlob("~/(abc)"), true); + assertEquals(!isGlob("+~(abc)"), true); + assertEquals(!isGlob("."), true); + assertEquals(!isGlob("@.(abc)"), true); + assertEquals(!isGlob("aa"), true); + assertEquals(!isGlob("who?"), true); + assertEquals(!isGlob("why!?"), true); + assertEquals(!isGlob("where???"), true); + assertEquals(!isGlob("abc!/def/!ghi.js"), true); + assertEquals(!isGlob("abc.js"), true); + assertEquals(!isGlob("abc/def/!ghi.js"), true); + assertEquals(!isGlob("abc/def/ghi.js"), true); + + // Should be true if path has regex capture group + assertEquals(isGlob("abc/(?!foo).js"), true); + assertEquals(isGlob("abc/(?:foo).js"), true); + assertEquals(isGlob("abc/(?=foo).js"), true); + assertEquals(isGlob("abc/(a|b).js"), true); + assertEquals(isGlob("abc/(a|b|c).js"), true); + assertEquals(isGlob("abc/(foo bar)/*.js"), true); + + // Should be false if the path has parens but is not a valid capture group + assertEquals(!isGlob("abc/(?foo).js"), true); + assertEquals(!isGlob("abc/(a b c).js"), true); + assertEquals(!isGlob("abc/(ab).js"), true); + assertEquals(!isGlob("abc/(abc).js"), true); + assertEquals(!isGlob("abc/(foo bar).js"), true); + + // should be false if the capture group is imbalanced + assertEquals(!isGlob("abc/(?ab.js"), true); + assertEquals(!isGlob("abc/(ab.js"), true); + assertEquals(!isGlob("abc/(a|b.js"), true); + assertEquals(!isGlob("abc/(a|b|c.js"), true); + + // should be true if the path has a regex character class + assertEquals(isGlob("abc/[abc].js"), true); + assertEquals(isGlob("abc/[^abc].js"), true); + assertEquals(isGlob("abc/[1-3].js"), true); + + // should be false if the character class is not balanced + assertEquals(!isGlob("abc/[abc.js"), true); + assertEquals(!isGlob("abc/[^abc.js"), true); + assertEquals(!isGlob("abc/[1-3.js"), true); + + // should be false if the character class is escaped + assertEquals(!isGlob("abc/\\[abc].js"), true); + assertEquals(!isGlob("abc/\\[^abc].js"), true); + assertEquals(!isGlob("abc/\\[1-3].js"), true); + + // should be true if the path has brace characters + assertEquals(isGlob("abc/{a,b}.js"), true); + assertEquals(isGlob("abc/{a..z}.js"), true); + assertEquals(isGlob("abc/{a..z..2}.js"), true); + + // should be false if (basic) braces are not balanced + assertEquals(!isGlob("abc/\\{a,b}.js"), true); + assertEquals(!isGlob("abc/\\{a..z}.js"), true); + assertEquals(!isGlob("abc/\\{a..z..2}.js"), true); + + // should be true if the path has regex characters + assertEquals(isGlob("!&(abc)"), true); + assertEquals(isGlob("!*.js"), true); + assertEquals(isGlob("!foo"), true); + assertEquals(isGlob("!foo.js"), true); + assertEquals(isGlob("**/abc.js"), true); + assertEquals(isGlob("*.js"), true); + assertEquals(isGlob("*z(abc)"), true); + assertEquals(isGlob("[1-10].js"), true); + assertEquals(isGlob("[^abc].js"), true); + assertEquals(isGlob("[a-j]*[^c]b/c"), true); + assertEquals(isGlob("[abc].js"), true); + assertEquals(isGlob("a/b/c/[a-z].js"), true); + assertEquals(isGlob("abc/(aaa|bbb).js"), true); + assertEquals(isGlob("abc/*.js"), true); + assertEquals(isGlob("abc/{a,b}.js"), true); + assertEquals(isGlob("abc/{a..z..2}.js"), true); + assertEquals(isGlob("abc/{a..z}.js"), true); + + assertEquals(!isGlob("$(abc)"), true); + assertEquals(!isGlob("&(abc)"), true); + assertEquals(!isGlob("Who?.js"), true); + assertEquals(!isGlob("? (abc)"), true); + assertEquals(!isGlob("?.js"), true); + assertEquals(!isGlob("abc/?.js"), true); + + // should be false if regex characters are escaped + assertEquals(!isGlob("\\?.js"), true); + assertEquals(!isGlob("\\[1-10\\].js"), true); + assertEquals(!isGlob("\\[^abc\\].js"), true); + assertEquals(!isGlob("\\[a-j\\]\\*\\[^c\\]b/c"), true); + assertEquals(!isGlob("\\[abc\\].js"), true); + assertEquals(!isGlob("\\a/b/c/\\[a-z\\].js"), true); + assertEquals(!isGlob("abc/\\(aaa|bbb).js"), true); + assertEquals(!isGlob("abc/\\?.js"), true); + } +}); + runIfMain(import.meta); From a0844c90f1c8b556e0a4123f379fd625aaa71d6a Mon Sep 17 00:00:00 2001 From: Shubhadeep Das Date: Thu, 23 May 2019 09:47:47 +0530 Subject: [PATCH 2/3] Updated isGlob tests --- fs/glob.ts | 5 +- fs/glob_test.ts | 173 ++++++++++++++++++++++++------------------------ 2 files changed, 88 insertions(+), 90 deletions(-) diff --git a/fs/glob.ts b/fs/glob.ts index fad874832d58..c96b61d6d7e6 100644 --- a/fs/glob.ts +++ b/fs/glob.ts @@ -46,14 +46,13 @@ export function glob(glob: string, options: GlobOptions = {}): RegExp { /** Test whether the given string is a glob */ export function isGlob(str: string): boolean { - const chars: any = { "{": "}", "(": ")", "[": "]" }; - const strictRegex: RegExp = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; + const chars: Record = { "{": "}", "(": ")", "[": "]" }; + const regex: RegExp = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; if (str === "") { return false; } - const regex = strictRegex; let match: RegExpExecArray | null; while ((match = regex.exec(str))) { diff --git a/fs/glob_test.ts b/fs/glob_test.ts index 61c2fbaf28d6..9151b9e9e6e1 100644 --- a/fs/glob_test.ts +++ b/fs/glob_test.ts @@ -1,7 +1,7 @@ const { mkdir } = Deno; type FileInfo = Deno.FileInfo; import { test, runIfMain } from "../testing/mod.ts"; -import { assertEquals } from "../testing/asserts.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"; @@ -142,115 +142,114 @@ test({ name: "isGlob: pattern to test", fn(): void { // should be true if valid glob pattern - assertEquals(isGlob("!foo.js"), true); - assertEquals(isGlob("*.js"), true); - assertEquals(isGlob("!*.js"), true); - assertEquals(isGlob("!foo"), true); - assertEquals(isGlob("!foo.js"), true); - assertEquals(isGlob("**/abc.js"), true); - assertEquals(isGlob("abc/*.js"), true); - assertEquals(isGlob("@.(?:abc)"), true); - assertEquals(isGlob("@.(?!abc)"), true); + 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 - assertEquals(!isGlob(""), true); - assertEquals(!isGlob(""), true); - assertEquals(!isGlob("~/abc"), true); - assertEquals(!isGlob("~/abc"), true); - assertEquals(!isGlob("~/(abc)"), true); - assertEquals(!isGlob("+~(abc)"), true); - assertEquals(!isGlob("."), true); - assertEquals(!isGlob("@.(abc)"), true); - assertEquals(!isGlob("aa"), true); - assertEquals(!isGlob("who?"), true); - assertEquals(!isGlob("why!?"), true); - assertEquals(!isGlob("where???"), true); - assertEquals(!isGlob("abc!/def/!ghi.js"), true); - assertEquals(!isGlob("abc.js"), true); - assertEquals(!isGlob("abc/def/!ghi.js"), true); - assertEquals(!isGlob("abc/def/ghi.js"), true); + 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 - assertEquals(isGlob("abc/(?!foo).js"), true); - assertEquals(isGlob("abc/(?:foo).js"), true); - assertEquals(isGlob("abc/(?=foo).js"), true); - assertEquals(isGlob("abc/(a|b).js"), true); - assertEquals(isGlob("abc/(a|b|c).js"), true); - assertEquals(isGlob("abc/(foo bar)/*.js"), true); + 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 - assertEquals(!isGlob("abc/(?foo).js"), true); - assertEquals(!isGlob("abc/(a b c).js"), true); - assertEquals(!isGlob("abc/(ab).js"), true); - assertEquals(!isGlob("abc/(abc).js"), true); - assertEquals(!isGlob("abc/(foo bar).js"), true); + 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 - assertEquals(!isGlob("abc/(?ab.js"), true); - assertEquals(!isGlob("abc/(ab.js"), true); - assertEquals(!isGlob("abc/(a|b.js"), true); - assertEquals(!isGlob("abc/(a|b|c.js"), true); + 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 - assertEquals(isGlob("abc/[abc].js"), true); - assertEquals(isGlob("abc/[^abc].js"), true); - assertEquals(isGlob("abc/[1-3].js"), true); + 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 - assertEquals(!isGlob("abc/[abc.js"), true); - assertEquals(!isGlob("abc/[^abc.js"), true); - assertEquals(!isGlob("abc/[1-3.js"), true); + 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 - assertEquals(!isGlob("abc/\\[abc].js"), true); - assertEquals(!isGlob("abc/\\[^abc].js"), true); - assertEquals(!isGlob("abc/\\[1-3].js"), true); + 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 - assertEquals(isGlob("abc/{a,b}.js"), true); - assertEquals(isGlob("abc/{a..z}.js"), true); - assertEquals(isGlob("abc/{a..z..2}.js"), true); + 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 - assertEquals(!isGlob("abc/\\{a,b}.js"), true); - assertEquals(!isGlob("abc/\\{a..z}.js"), true); - assertEquals(!isGlob("abc/\\{a..z..2}.js"), true); + 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 - assertEquals(isGlob("!&(abc)"), true); - assertEquals(isGlob("!*.js"), true); - assertEquals(isGlob("!foo"), true); - assertEquals(isGlob("!foo.js"), true); - assertEquals(isGlob("**/abc.js"), true); - assertEquals(isGlob("*.js"), true); - assertEquals(isGlob("*z(abc)"), true); - assertEquals(isGlob("[1-10].js"), true); - assertEquals(isGlob("[^abc].js"), true); - assertEquals(isGlob("[a-j]*[^c]b/c"), true); - assertEquals(isGlob("[abc].js"), true); - assertEquals(isGlob("a/b/c/[a-z].js"), true); - assertEquals(isGlob("abc/(aaa|bbb).js"), true); - assertEquals(isGlob("abc/*.js"), true); - assertEquals(isGlob("abc/{a,b}.js"), true); - assertEquals(isGlob("abc/{a..z..2}.js"), true); - assertEquals(isGlob("abc/{a..z}.js"), true); + 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")); - assertEquals(!isGlob("$(abc)"), true); - assertEquals(!isGlob("&(abc)"), true); - assertEquals(!isGlob("Who?.js"), true); - assertEquals(!isGlob("? (abc)"), true); - assertEquals(!isGlob("?.js"), true); - assertEquals(!isGlob("abc/?.js"), true); + 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 - assertEquals(!isGlob("\\?.js"), true); - assertEquals(!isGlob("\\[1-10\\].js"), true); - assertEquals(!isGlob("\\[^abc\\].js"), true); - assertEquals(!isGlob("\\[a-j\\]\\*\\[^c\\]b/c"), true); - assertEquals(!isGlob("\\[abc\\].js"), true); - assertEquals(!isGlob("\\a/b/c/\\[a-z\\].js"), true); - assertEquals(!isGlob("abc/\\(aaa|bbb).js"), true); - assertEquals(!isGlob("abc/\\?.js"), true); + 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")); } }); From 25404dcee48ee8a72dbc67319d48c03d5db15960 Mon Sep 17 00:00:00 2001 From: Shubhadeep Das Date: Thu, 23 May 2019 11:47:39 +0530 Subject: [PATCH 3/3] Removed RegExp type --- fs/glob.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/glob.ts b/fs/glob.ts index c96b61d6d7e6..8d322b761a40 100644 --- a/fs/glob.ts +++ b/fs/glob.ts @@ -47,7 +47,7 @@ export function glob(glob: string, options: GlobOptions = {}): RegExp { /** Test whether the given string is a glob */ export function isGlob(str: string): boolean { const chars: Record = { "{": "}", "(": ")", "[": "]" }; - const regex: RegExp = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; + const regex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; if (str === "") { return false;