summaryrefslogtreecommitdiff
path: root/std/path
diff options
context:
space:
mode:
Diffstat (limited to 'std/path')
-rw-r--r--std/path/_constants.ts14
-rw-r--r--std/path/_globrex.ts328
-rw-r--r--std/path/_globrex_test.ts825
-rw-r--r--std/path/glob.ts274
-rw-r--r--std/path/glob_test.ts582
5 files changed, 698 insertions, 1325 deletions
diff --git a/std/path/_constants.ts b/std/path/_constants.ts
index 186c32ab5..920979432 100644
--- a/std/path/_constants.ts
+++ b/std/path/_constants.ts
@@ -47,14 +47,16 @@ export const CHAR_EQUAL = 61; /* = */
export const CHAR_0 = 48; /* 0 */
export const CHAR_9 = 57; /* 9 */
+let NATIVE_OS: typeof Deno.build.os = "linux";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const navigator = (globalThis as any).navigator;
-
-let isWindows = false;
if (globalThis.Deno != null) {
- isWindows = Deno.build.os == "windows";
-} else if (navigator?.appVersion != null) {
- isWindows = navigator.appVersion.includes("Win");
+ NATIVE_OS = Deno.build.os;
+} else if (navigator?.appVersion?.includes?.("Win") ?? false) {
+ NATIVE_OS = "windows";
}
+// TODO(nayeemrmn): Improve OS detection in browsers beyond Windows.
+
+export const isWindows = NATIVE_OS == "windows";
-export { isWindows };
+export { NATIVE_OS };
diff --git a/std/path/_globrex.ts b/std/path/_globrex.ts
deleted file mode 100644
index 0cc5399fa..000000000
--- a/std/path/_globrex.ts
+++ /dev/null
@@ -1,328 +0,0 @@
-// This file is ported from globrex@0.1.2
-// MIT License
-// Copyright (c) 2018 Terkel Gjervig Nielsen
-/** This module is browser compatible. */
-
-import { isWindows as isWin } from "./_constants.ts";
-
-const SEP = isWin ? `(?:\\\\|\\/)` : `\\/`;
-const SEP_ESC = isWin ? `\\\\` : `/`;
-const SEP_RAW = isWin ? `\\` : `/`;
-const GLOBSTAR = `(?:(?:[^${SEP_ESC}/]*(?:${SEP_ESC}|\/|$))*)`;
-const WILDCARD = `(?:[^${SEP_ESC}/]*)`;
-const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}/]*(?:${SEP_ESC}|\/|$))*)`;
-const WILDCARD_SEGMENT = `(?:[^${SEP_ESC}/]*)`;
-
-export interface GlobrexOptions {
- /** Allow ExtGlob features.
- * @default false */
- extended?: boolean;
- /** Support globstar.
- * @remarks When globstar is `true`, '/foo/**' is equivalent
- * to '/foo/*' when globstar is `false`.
- * Having globstar set to `true` is the same usage as
- * using wildcards in bash.
- * @default false */
- globstar?: boolean;
- /** Be laissez-faire about mutiple slashes.
- * @default true */
- strict?: boolean;
- /** Parse as filepath for extra path related features.
- * @default false */
- filepath?: boolean;
- /** Flag to use in the generated RegExp. */
- flags?: string;
-}
-
-export interface GlobrexResult {
- regex: RegExp;
- path?: {
- regex: RegExp;
- segments: RegExp[];
- globstar?: RegExp;
- };
-}
-
-/**
- * Convert any glob pattern to a JavaScript Regexp object
- * @param glob Glob pattern to convert
- * @param opts Configuration object
- * @returns Converted object with string, segments and RegExp object
- */
-export function globrex(
- glob: string,
- {
- extended = false,
- globstar = false,
- strict = false,
- filepath = false,
- flags = "",
- }: GlobrexOptions = {},
-): GlobrexResult {
- const sepPattern = new RegExp(`^${SEP}${strict ? "" : "+"}$`);
- let regex = "";
- let segment = "";
- let pathRegexStr = "";
- const pathSegments = [];
-
- // 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: string,
- options: AddOptions = { split: false, last: false, only: "" },
- ): void {
- const { split, last, only } = options;
- if (only !== "path") regex += str;
- if (filepath && only !== "regex") {
- pathRegexStr += str.match(sepPattern) ? SEP : str;
- if (split) {
- if (last) segment += str;
- if (segment !== "") {
- // change it 'includes'
- if (!flags.includes("g")) segment = `^${segment}$`;
- pathSegments.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.match(sepPattern)) {
- add(SEP, { split: true });
- if (n != null && n.match(sepPattern) && !strict) regex += "?";
- continue;
- }
-
- if (c === "(") {
- if (ext.length) {
- add(`${c}?:`);
- continue;
- }
- add(`\\${c}`);
- continue;
- }
-
- if (c === ")") {
- if (ext.length) {
- add(c);
- const type: string | undefined = ext.pop();
- if (type === "@") {
- add("{1}");
- } else if (type === "!") {
- add(WILDCARD);
- } else {
- add(type as string);
- }
- 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
- const prevChar = glob[i - 1];
- let starCount = 1;
- while (glob[i + 1] === "*") {
- starCount++;
- i++;
- }
- const 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
- const isGlobstar = starCount > 1 && // multiple "*"'s
- // from the start of the segment
- [SEP_RAW, "/", undefined].includes(prevChar) &&
- // to the end of the segment
- [SEP_RAW, "/", undefined].includes(nextChar);
- 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) pathRegexStr = `^${pathRegexStr}$`;
- }
-
- const result: GlobrexResult = { regex: new RegExp(regex, flags) };
-
- // Push the last segment
- if (filepath) {
- pathSegments.push(new RegExp(segment, flags));
- result.path = {
- regex: new RegExp(pathRegexStr, flags),
- segments: pathSegments,
- globstar: new RegExp(
- !flags.includes("g") ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT,
- flags,
- ),
- };
- }
-
- return result;
-}
diff --git a/std/path/_globrex_test.ts b/std/path/_globrex_test.ts
deleted file mode 100644
index 19eabc983..000000000
--- a/std/path/_globrex_test.ts
+++ /dev/null
@@ -1,825 +0,0 @@
-// This file is ported from globrex@0.1.2
-// MIT License
-// Copyright (c) 2018 Terkel Gjervig Nielsen
-import { assertEquals } from "../testing/asserts.ts";
-import { GlobrexOptions, globrex } from "./_globrex.ts";
-
-const isWin = Deno.build.os === "windows";
-const t = { equal: assertEquals, is: assertEquals };
-
-function match(
- glob: string,
- strUnix: string,
- strWin?: string | object,
- opts: GlobrexOptions = {},
-): boolean {
- if (typeof strWin === "object") {
- opts = strWin;
- strWin = "";
- }
- const { regex } = globrex(glob, opts);
- const match = (isWin && strWin ? strWin : strUnix).match(regex);
- if (match && !regex.flags.includes("g")) {
- assertEquals(match.length, 1);
- }
- return !!match;
-}
-
-Deno.test({
- name: "globrex: standard",
- fn(): void {
- const res = globrex("*.js");
- t.equal(typeof globrex, "function", "constructor is a typeof function");
- t.equal(res instanceof Object, true, "returns object");
- t.equal(res.regex.toString(), "/^.*\\.js$/", "returns regex object");
- },
-});
-
-Deno.test({
- name: "globrex: Standard * matching",
- fn(): void {
- t.equal(match("*", "foo"), true, "match everything");
- t.equal(match("*", "foo", { flags: "g" }), true, "match everything");
- t.equal(match("f*", "foo"), true, "match the end");
- t.equal(match("f*", "foo", { flags: "g" }), true, "match the end");
- t.equal(match("*o", "foo"), true, "match the start");
- t.equal(match("*o", "foo", { flags: "g" }), true, "match the start");
- t.equal(match("u*orn", "unicorn"), true, "match the middle");
- t.equal(
- match("u*orn", "unicorn", { flags: "g" }),
- true,
- "match the middle",
- );
- t.equal(match("ico", "unicorn"), false, "do not match without g");
- t.equal(
- match("ico", "unicorn", { flags: "g" }),
- true,
- 'match anywhere with RegExp "g"',
- );
- t.equal(match("u*nicorn", "unicorn"), true, "match zero characters");
- t.equal(
- match("u*nicorn", "unicorn", { flags: "g" }),
- true,
- "match zero characters",
- );
- },
-});
-
-Deno.test({
- name: "globrex: advance * matching",
- fn(): void {
- t.equal(
- match("*.min.js", "http://example.com/jquery.min.js", {
- globstar: false,
- }),
- true,
- "complex match",
- );
- t.equal(
- match("*.min.*", "http://example.com/jquery.min.js", { globstar: false }),
- true,
- "complex match",
- );
- t.equal(
- match("*/js/*.js", "http://example.com/js/jquery.min.js", {
- globstar: false,
- }),
- true,
- "complex match",
- );
- t.equal(
- match("*.min.*", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- "complex match global",
- );
- t.equal(
- match("*.min.js", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- "complex match global",
- );
- t.equal(
- match("*/js/*.js", "http://example.com/js/jquery.min.js", { flags: "g" }),
- true,
- "complex match global",
- );
-
- const str = "\\/$^+?.()=!|{},[].*";
- t.equal(match(str, str), true, "battle test complex string - strict");
- t.equal(
- match(str, str, { flags: "g" }),
- true,
- "battle test complex string - strict",
- );
-
- t.equal(
- match(".min.", "http://example.com/jquery.min.js"),
- false,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("*.min.*", "http://example.com/jquery.min.js"),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match(".min.", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("http:", "http://example.com/jquery.min.js"),
- false,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("http:*", "http://example.com/jquery.min.js"),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("http:", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("min.js", "http://example.com/jquery.min.js"),
- false,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("*.min.js", "http://example.com/jquery.min.js"),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("min.js", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- 'matches without/with using RegExp "g"',
- );
- t.equal(
- match("min", "http://example.com/jquery.min.js", { flags: "g" }),
- true,
- 'match anywhere (globally) using RegExp "g"',
- );
- t.equal(
- match("/js/", "http://example.com/js/jquery.min.js", { flags: "g" }),
- true,
- 'match anywhere (globally) using RegExp "g"',
- );
- t.equal(match("/js*jq*.js", "http://example.com/js/jquery.min.js"), false);
- t.equal(
- match("/js*jq*.js", "http://example.com/js/jquery.min.js", {
- flags: "g",
- }),
- true,
- );
- },
-});
-
-Deno.test({
- name: "globrex: ? match one character, no more and no less",
- fn(): void {
- t.equal(match("f?o", "foo", { extended: true }), true);
- t.equal(match("f?o", "fooo", { extended: true }), false);
- t.equal(match("f?oo", "foo", { extended: true }), false);
-
- const tester = (globstar: boolean): void => {
- t.equal(
- match("f?o", "foo", { extended: true, globstar, flags: "g" }),
- true,
- );
- t.equal(
- match("f?o", "fooo", { extended: true, globstar, flags: "g" }),
- true,
- );
- t.equal(
- match("f?o?", "fooo", { extended: true, globstar, flags: "g" }),
- true,
- );
-
- t.equal(
- match("?fo", "fooo", { extended: true, globstar, flags: "g" }),
- false,
- );
- t.equal(
- match("f?oo", "foo", { extended: true, globstar, flags: "g" }),
- false,
- );
- t.equal(
- match("foo?", "foo", { extended: true, globstar, flags: "g" }),
- false,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: [] match a character range",
- fn(): void {
- t.equal(match("fo[oz]", "foo", { extended: true }), true);
- t.equal(match("fo[oz]", "foz", { extended: true }), true);
- t.equal(match("fo[oz]", "fog", { extended: true }), false);
- t.equal(match("fo[a-z]", "fob", { extended: true }), true);
- t.equal(match("fo[a-d]", "fot", { extended: true }), false);
- t.equal(match("fo[!tz]", "fot", { extended: true }), false);
- t.equal(match("fo[!tz]", "fob", { extended: true }), true);
-
- const tester = (globstar: boolean): void => {
- t.equal(
- match("fo[oz]", "foo", { extended: true, globstar, flags: "g" }),
- true,
- );
- t.equal(
- match("fo[oz]", "foz", { extended: true, globstar, flags: "g" }),
- true,
- );
- t.equal(
- match("fo[oz]", "fog", { extended: true, globstar, flags: "g" }),
- false,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: [] extended character ranges",
- fn(): void {
- t.equal(
- match("[[:alnum:]]/bar.txt", "a/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "11/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "a/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "b/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "c/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "abc/bar.txt", { extended: true }),
- false,
- );
- t.equal(
- match("@([[:alnum:]abc]|11)/bar.txt", "3/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]]/bar.txt", "1/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]b]/bar.txt", "b/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("[![:digit:]b]/bar.txt", "a/bar.txt", { extended: true }),
- true,
- );
- t.equal(
- match("[[:alnum:]]/bar.txt", "!/bar.txt", { extended: true }),
- false,
- );
- t.equal(
- match("[[:digit:]]/bar.txt", "a/bar.txt", { extended: true }),
- false,
- );
- t.equal(
- match("[[:digit:]b]/bar.txt", "a/bar.txt", { extended: true }),
- false,
- );
- },
-});
-
-Deno.test({
- name: "globrex: {} match a choice of different substrings",
- fn(): void {
- t.equal(match("foo{bar,baaz}", "foobaaz", { extended: true }), true);
- t.equal(match("foo{bar,baaz}", "foobar", { extended: true }), true);
- t.equal(match("foo{bar,baaz}", "foobuzz", { extended: true }), false);
- t.equal(match("foo{bar,b*z}", "foobuzz", { extended: true }), true);
-
- const tester = (globstar: boolean): void => {
- t.equal(
- match("foo{bar,baaz}", "foobaaz", {
- extended: true,
- globstar,
- flag: "g",
- }),
- true,
- );
- t.equal(
- match("foo{bar,baaz}", "foobar", {
- extended: true,
- globstar,
- flag: "g",
- }),
- true,
- );
- t.equal(
- match("foo{bar,baaz}", "foobuzz", {
- extended: true,
- globstar,
- flag: "g",
- }),
- false,
- );
- t.equal(
- match("foo{bar,b*z}", "foobuzz", {
- extended: true,
- globstar,
- flag: "g",
- }),
- true,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: complex extended matches",
- fn(): void {
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://foo.baaz.com/jquery.min.js",
- { extended: true },
- ),
- true,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.buzz.com/index.html",
- { extended: true },
- ),
- true,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.buzz.com/index.htm",
- { extended: true },
- ),
- false,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.bar.com/index.html",
- { extended: true },
- ),
- false,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://flozz.buzz.com/index.html",
- { extended: true },
- ),
- false,
- );
-
- const tester = (globstar: boolean): void => {
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://foo.baaz.com/jquery.min.js",
- { extended: true, globstar, flags: "g" },
- ),
- true,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.buzz.com/index.html",
- { extended: true, globstar, flags: "g" },
- ),
- true,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.buzz.com/index.htm",
- { extended: true, globstar, flags: "g" },
- ),
- false,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://moz.bar.com/index.html",
- { extended: true, globstar, flags: "g" },
- ),
- false,
- );
- t.equal(
- match(
- "http://?o[oz].b*z.com/{*.js,*.html}",
- "http://flozz.buzz.com/index.html",
- { extended: true, globstar, flags: "g" },
- ),
- false,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: standard globstar",
- fn(): void {
- const tester = (globstar: boolean): void => {
- t.equal(
- match(
- "http://foo.com/**/{*.js,*.html}",
- "http://foo.com/bar/jquery.min.js",
- { extended: true, globstar, flags: "g" },
- ),
- true,
- );
- t.equal(
- match(
- "http://foo.com/**/{*.js,*.html}",
- "http://foo.com/bar/baz/jquery.min.js",
- { extended: true, globstar, flags: "g" },
- ),
- true,
- );
- t.equal(
- match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js", {
- extended: true,
- globstar,
- flags: "g",
- }),
- true,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: remaining chars should match themself",
- fn(): void {
- const tester = (globstar: boolean): void => {
- const testExtStr = "\\/$^+.()=!|,.*";
- t.equal(match(testExtStr, testExtStr, { extended: true }), true);
- t.equal(
- match(testExtStr, testExtStr, { extended: true, globstar, flags: "g" }),
- true,
- );
- };
-
- tester(true);
- tester(false);
- },
-});
-
-Deno.test({
- name: "globrex: globstar advance testing",
- fn(): void {
- t.equal(match("/foo/*", "/foo/bar.txt", { globstar: true }), true);
- t.equal(match("/foo/**", "/foo/bar.txt", { globstar: true }), true);
- t.equal(match("/foo/**", "/foo/bar/baz.txt", { globstar: true }), true);
- t.equal(match("/foo/**", "/foo/bar/baz.txt", { globstar: true }), true);
- t.equal(
- match("/foo/*/*.txt", "/foo/bar/baz.txt", { globstar: true }),
- true,
- );
- t.equal(
- match("/foo/**/*.txt", "/foo/bar/baz.txt", { globstar: true }),
- true,
- );
- t.equal(
- match("/foo/**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- true,
- );
- t.equal(match("/foo/**/bar.txt", "/foo/bar.txt", { globstar: true }), true);
- t.equal(
- match("/foo/**/**/bar.txt", "/foo/bar.txt", { globstar: true }),
- true,
- );
- t.equal(
- match("/foo/**/*/baz.txt", "/foo/bar/baz.txt", { globstar: true }),
- true,
- );
- t.equal(match("/foo/**/*.txt", "/foo/bar.txt", { globstar: true }), true);
- t.equal(
- match("/foo/**/**/*.txt", "/foo/bar.txt", { globstar: true }),
- true,
- );
- t.equal(
- match("/foo/**/*/*.txt", "/foo/bar/baz.txt", { globstar: true }),
- true,
- );
- t.equal(
- match("**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- true,
- );
- t.equal(match("**/foo.txt", "foo.txt", { globstar: true }), true);
- t.equal(match("**/*.txt", "foo.txt", { globstar: true }), true);
- t.equal(match("/foo/*", "/foo/bar/baz.txt", { globstar: true }), false);
- t.equal(match("/foo/*.txt", "/foo/bar/baz.txt", { globstar: true }), false);
- t.equal(
- match("/foo/*/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- false,
- );
- t.equal(match("/foo/*/bar.txt", "/foo/bar.txt", { globstar: true }), false);
- t.equal(
- match("/foo/*/*/baz.txt", "/foo/bar/baz.txt", { globstar: true }),
- false,
- );
- t.equal(
- match("/foo/**.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- false,
- );
- t.equal(
- match("/foo/bar**/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- false,
- );
- t.equal(match("/foo/bar**", "/foo/bar/baz.txt", { globstar: true }), false);
- t.equal(
- match("**/.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- false,
- );
- t.equal(
- match("*/*.txt", "/foo/bar/baz/qux.txt", { globstar: true }),
- false,
- );
- t.equal(match("*/*.txt", "foo.txt", { globstar: true }), false);
- t.equal(
- match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", {
- extended: true,
- globstar: true,
- }),
- false,
- );
- t.equal(
- match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", {
- globstar: true,
- }),
- false,
- );
- t.equal(
- match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js", {
- globstar: false,
- }),
- true,
- );
- t.equal(
- match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js", {
- globstar: true,
- }),
- true,
- );
- t.equal(
- match(
- "http://foo.com/*/*/jquery.min.js",
- "http://foo.com/bar/baz/jquery.min.js",
- { globstar: true },
- ),
- true,
- );
- t.equal(
- match(
- "http://foo.com/**/jquery.min.js",
- "http://foo.com/bar/baz/jquery.min.js",
- { globstar: true },
- ),
- true,
- );
- t.equal(
- match(
- "http://foo.com/*/*/jquery.min.js",
- "http://foo.com/bar/baz/jquery.min.js",
- { globstar: false },
- ),
- true,
- );
- t.equal(
- match(
- "http://foo.com/*/jquery.min.js",
- "http://foo.com/bar/baz/jquery.min.js",
- { globstar: false },
- ),
- true,
- );
- t.equal(
- match(
- "http://foo.com/*/jquery.min.js",
- "http://foo.com/bar/baz/jquery.min.js",
- { globstar: true },
- ),
- false,
- );
- },
-});
-
-Deno.test({
- name: "globrex: extended extglob ?",
- fn(): void {
- t.equal(match("(foo).txt", "(foo).txt", { extended: true }), true);
- t.equal(match("?(foo).txt", "foo.txt", { extended: true }), true);
- t.equal(match("?(foo).txt", ".txt", { extended: true }), true);
- t.equal(match("?(foo|bar)baz.txt", "foobaz.txt", { extended: true }), true);
- t.equal(
- match("?(ba[zr]|qux)baz.txt", "bazbaz.txt", { extended: true }),
- true,
- );
- t.equal(
- match("?(ba[zr]|qux)baz.txt", "barbaz.txt", { extended: true }),
- true,
- );
- t.equal(
- match("?(ba[zr]|qux)baz.txt", "quxbaz.txt", { extended: true }),
- true,
- );
- t.equal(
- match("?(ba[!zr]|qux)baz.txt", "batbaz.txt", { extended: true }),
- true,
- );
- t.equal(match("?(ba*|qux)baz.txt", "batbaz.txt", { extended: true }), true);
- t.equal(
- match("?(ba*|qux)baz.txt", "batttbaz.txt", { extended: true }),
- true,
- );
- t.equal(match("?(ba*|qux)baz.txt", "quxbaz.txt", { extended: true }), true);
- t.equal(
- match("?(ba?(z|r)|qux)baz.txt", "bazbaz.txt", { extended: true }),
- true,
- );
- t.equal(
- match("?(ba?(z|?(r))|qux)baz.txt", "bazbaz.txt", { extended: true }),
- true,
- );
- t.equal(match("?(foo).txt", "foo.txt", { extended: false }), false);
- t.equal(
- match("?(foo|bar)baz.txt", "foobarbaz.txt", { extended: true }),
- false,
- );
- t.equal(
- match("?(ba[zr]|qux)baz.txt", "bazquxbaz.txt", { extended: true }),
- false,
- );
- t.equal(
- match("?(ba[!zr]|qux)baz.txt", "bazbaz.txt", { extended: true }),
- false,
- );
- },
-});
-
-Deno.test({
- name: "globrex: extended extglob *",
- fn(): void {
- t.equal(match("*(foo).txt", "foo.txt", { extended: true }), true);
- t.equal(match("*foo.txt", "bofoo.txt", { extended: true }), true);
- t.equal(match("*(foo).txt", "foofoo.txt", { extended: true }), true);
- t.equal(match("*(foo).txt", ".txt", { extended: true }), true);
- t.equal(match("*(fooo).txt", ".txt", { extended: true }), true);
- t.equal(match("*(fooo).txt", "foo.txt", { extended: true }), false);
- t.equal(match("*(foo|bar).txt", "foobar.txt", { extended: true }), true);
- t.equal(match("*(foo|bar).txt", "barbar.txt", { extended: true }), true);
- t.equal(match("*(foo|bar).txt", "barfoobar.txt", { extended: true }), true);
- t.equal(match("*(foo|bar).txt", ".txt", { extended: true }), true);
- t.equal(match("*(foo|ba[rt]).txt", "bat.txt", { extended: true }), true);
- t.equal(match("*(foo|b*[rt]).txt", "blat.txt", { extended: true }), true);
- t.equal(match("*(foo|b*[rt]).txt", "tlat.txt", { extended: true }), false);
- t.equal(
- match("*(*).txt", "whatever.txt", { extended: true, globstar: true }),
- true,
- );
- t.equal(
- match("*(foo|bar)/**/*.txt", "foo/hello/world/bar.txt", {
- extended: true,
- globstar: true,
- }),
- true,
- );
- t.equal(
- match("*(foo|bar)/**/*.txt", "foo/world/bar.txt", {
- extended: true,
- globstar: true,
- }),
- true,
- );
- },
-});
-
-Deno.test({
- name: "globrex: extended extglob +",
- fn(): void {
- t.equal(match("+(foo).txt", "foo.txt", { extended: true }), true);
- t.equal(match("+foo.txt", "+foo.txt", { extended: true }), true);
- t.equal(match("+(foo).txt", ".txt", { extended: true }), false);
- t.equal(match("+(foo|bar).txt", "foobar.txt", { extended: true }), true);
- },
-});
-
-Deno.test({
- name: "globrex: extended extglob @",
- fn(): void {
- t.equal(match("@(foo).txt", "foo.txt", { extended: true }), true);
- t.equal(match("@foo.txt", "@foo.txt", { extended: true }), true);
- t.equal(match("@(foo|baz)bar.txt", "foobar.txt", { extended: true }), true);
- t.equal(
- match("@(foo|baz)bar.txt", "foobazbar.txt", { extended: true }),
- false,
- );
- t.equal(
- match("@(foo|baz)bar.txt", "foofoobar.txt", { extended: true }),
- false,
- );
- t.equal(
- match("@(foo|baz)bar.txt", "toofoobar.txt", { extended: true }),
- false,
- );
- },
-});
-
-Deno.test({
- name: "globrex: extended extglob !",
- fn(): void {
- t.equal(match("!(boo).txt", "foo.txt", { extended: true }), true);
- t.equal(match("!(foo|baz)bar.txt", "buzbar.txt", { extended: true }), true);
- t.equal(match("!bar.txt", "!bar.txt", { extended: true }), true);
- t.equal(
- match("!({foo,bar})baz.txt", "notbaz.txt", { extended: true }),
- true,
- );
- t.equal(
- match("!({foo,bar})baz.txt", "foobaz.txt", { extended: true }),
- false,
- );
- },
-});
-
-Deno.test({
- name: "globrex: strict",
- fn(): void {
- t.equal(match("foo//bar.txt", "foo/bar.txt"), true);
- t.equal(match("foo///bar.txt", "foo/bar.txt"), true);
- t.equal(match("foo///bar.txt", "foo/bar.txt", { strict: true }), false);
- },
-});
-
-Deno.test({
- name: "globrex: stress testing",
- fn(): void {
- t.equal(
- match("**/*/?yfile.{md,js,txt}", "foo/bar/baz/myfile.md", {
- extended: true,
- }),
- true,
- );
- t.equal(
- match("**/*/?yfile.{md,js,txt}", "foo/baz/myfile.md", { extended: true }),
- true,
- );
- t.equal(
- match("**/*/?yfile.{md,js,txt}", "foo/baz/tyfile.js", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]_.]/file.js", "1/file.js", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]_.]/file.js", "2/file.js", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]_.]/file.js", "_/file.js", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]_.]/file.js", "./file.js", { extended: true }),
- true,
- );
- t.equal(
- match("[[:digit:]_.]/file.js", "z/file.js", { extended: true }),
- false,
- );
- },
-});
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 */
diff --git a/std/path/glob_test.ts b/std/path/glob_test.ts
index dba79c302..bb3f8add0 100644
--- a/std/path/glob_test.ts
+++ b/std/path/glob_test.ts
@@ -1,130 +1,456 @@
import { assert, assertEquals } from "../testing/asserts.ts";
-import { testWalk, touch, walkArray } from "../fs/walk_test.ts";
-import { globToRegExp, isGlob, joinGlobs, normalizeGlob } from "./glob.ts";
-import { SEP, join } from "./mod.ts";
-
-Deno.test({
- name: "glob: glob to regex",
- fn(): void {
- assertEquals(globToRegExp("unicorn.*") instanceof RegExp, true);
- assertEquals(globToRegExp("unicorn.*").test("poney.ts"), false);
- assertEquals(globToRegExp("unicorn.*").test("unicorn.py"), true);
- assertEquals(globToRegExp("*.ts").test("poney.ts"), true);
- assertEquals(globToRegExp("*.ts").test("unicorn.js"), false);
- assertEquals(
- globToRegExp(join("unicorn", "**", "cathedral.ts")).test(
- join("unicorn", "in", "the", "cathedral.ts"),
- ),
- true,
- );
- assertEquals(
- globToRegExp(join("unicorn", "**", "cathedral.ts")).test(
- join("unicorn", "in", "the", "kitchen.ts"),
- ),
- false,
- );
- assertEquals(
- globToRegExp(join("unicorn", "**", "bathroom.*")).test(
- join("unicorn", "sleeping", "in", "bathroom.py"),
- ),
- true,
- );
- assertEquals(
- globToRegExp(join("unicorn", "!(sleeping)", "bathroom.ts"), {
- extended: true,
- }).test(join("unicorn", "flying", "bathroom.ts")),
- true,
- );
- assertEquals(
- globToRegExp(join("unicorn", "(!sleeping)", "bathroom.ts"), {
- extended: true,
- }).test(join("unicorn", "sleeping", "bathroom.ts")),
- false,
- );
- },
-});
-
-testWalk(
- async (d: string): Promise<void> => {
- await Deno.mkdir(d + "/a");
- await Deno.mkdir(d + "/b");
- await touch(d + "/a/x.ts");
- await touch(d + "/b/z.ts");
- await touch(d + "/b/z.js");
- },
- async function globInWalkWildcard(): Promise<void> {
- const arr = await walkArray(".", {
- match: [globToRegExp(join("*", "*.ts"))],
- });
- assertEquals(arr.length, 2);
- assertEquals(arr[0], "a/x.ts");
- assertEquals(arr[1], "b/z.ts");
- },
-);
-
-testWalk(
- async (d: string): Promise<void> => {
- await Deno.mkdir(d + "/a");
- await Deno.mkdir(d + "/a/yo");
- await touch(d + "/a/yo/x.ts");
- },
- async function globInWalkFolderWildcard(): Promise<void> {
- const arr = await walkArray(".", {
- match: [
- globToRegExp(join("a", "**", "*.ts"), {
- flags: "g",
- globstar: true,
- }),
- ],
- });
- assertEquals(arr.length, 1);
- assertEquals(arr[0], "a/yo/x.ts");
- },
-);
-
-testWalk(
- async (d: string): Promise<void> => {
- await Deno.mkdir(d + "/a");
- await Deno.mkdir(d + "/a/unicorn");
- await Deno.mkdir(d + "/a/deno");
- await Deno.mkdir(d + "/a/raptor");
- await touch(d + "/a/raptor/x.ts");
- await touch(d + "/a/deno/x.ts");
- await touch(d + "/a/unicorn/x.ts");
- },
- async function globInWalkFolderExtended(): Promise<void> {
- const arr = await walkArray(".", {
- match: [
- globToRegExp(join("a", "+(raptor|deno)", "*.ts"), {
- flags: "g",
- extended: true,
- }),
- ],
- });
- assertEquals(arr.length, 2);
- assertEquals(arr[0], "a/deno/x.ts");
- assertEquals(arr[1], "a/raptor/x.ts");
- },
-);
-
-testWalk(
- async (d: string): Promise<void> => {
- await touch(d + "/x.ts");
- await touch(d + "/x.js");
- await touch(d + "/b.js");
- },
- async function globInWalkWildcardExtension(): Promise<void> {
- const arr = await walkArray(".", {
- match: [globToRegExp("x.*", { flags: "g", globstar: true })],
- });
- assertEquals(arr.length, 2);
- assertEquals(arr[0], "x.js");
- assertEquals(arr[1], "x.ts");
- },
-);
-
-Deno.test({
- name: "isGlob: pattern to test",
+import {
+ GlobToRegExpOptions,
+ globToRegExp,
+ isGlob,
+ joinGlobs,
+ normalizeGlob,
+} from "./glob.ts";
+import { SEP } from "./mod.ts";
+
+function match(
+ glob: string,
+ path: string,
+ opts: GlobToRegExpOptions = {},
+): boolean {
+ if (opts.os == null) {
+ const matchDarwin = path.match(
+ globToRegExp(glob, { ...opts, os: "darwin" }),
+ );
+ if (matchDarwin) {
+ assertEquals(matchDarwin.length, 1);
+ }
+ const matchLinux = path.match(globToRegExp(glob, { ...opts, os: "linux" }));
+ if (matchLinux) {
+ assertEquals(matchLinux.length, 1);
+ }
+ const matchWindows = path.match(
+ globToRegExp(glob, { ...opts, os: "windows" }),
+ );
+ if (matchWindows) {
+ assertEquals(matchWindows.length, 1);
+ }
+ return !!matchDarwin && !!matchLinux && !!matchWindows;
+ } else {
+ const match = path.match(globToRegExp(glob, opts));
+ if (match) {
+ assertEquals(match.length, 1);
+ }
+ return !!match;
+ }
+}
+
+Deno.test({
+ name: "[path] globToRegExp() Basic RegExp",
+ fn(): void {
+ assertEquals(globToRegExp(""), /^$/);
+ assertEquals(globToRegExp("*.js", { os: "linux" }), /^[^/]*\.js\/*$/);
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() * (wildcard)",
+ fn(): void {
+ assert(match("*", "foo", { extended: false, globstar: false }));
+ assert(match("*", "foo", { extended: false, globstar: false }));
+ assert(match("f*", "foo", { extended: false, globstar: false }));
+ assert(match("f*", "foo", { extended: false, globstar: false }));
+ assert(match("*o", "foo", { extended: false, globstar: false }));
+ assert(match("*o", "foo", { extended: false, globstar: false }));
+ assert(match("u*orn", "unicorn", { extended: false, globstar: false }));
+ assert(match("u*orn", "unicorn", { extended: false, globstar: false }));
+ assert(!match("ico", "unicorn", { extended: false, globstar: false }));
+ assert(match("u*nicorn", "unicorn", { extended: false, globstar: false }));
+ assert(match("u*nicorn", "unicorn", { extended: false, globstar: false }));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() ? (match one character)",
+ fn(): void {
+ assert(match("f?o", "foo", { extended: false, globstar: false }));
+ assert(match("f?o?", "fooo", { extended: false, globstar: false }));
+ assert(!match("f?oo", "foo", { extended: false, globstar: false }));
+ assert(!match("?fo", "fooo", { extended: false, globstar: false }));
+ assert(!match("f?oo", "foo", { extended: false, globstar: false }));
+ assert(!match("foo?", "foo", { extended: false, globstar: false }));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() [seq] (character range)",
+ fn(): void {
+ assert(match("fo[oz]", "foo", { extended: false, globstar: false }));
+ assert(match("fo[oz]", "foz", { extended: false, globstar: false }));
+ assert(!match("fo[oz]", "fog", { extended: false, globstar: false }));
+ assert(match("fo[a-z]", "fob", { extended: false, globstar: false }));
+ assert(!match("fo[a-d]", "fot", { extended: false, globstar: false }));
+ assert(!match("fo[!tz]", "fot", { extended: false, globstar: false }));
+ assert(match("fo[!tz]", "fob", { extended: false, globstar: false }));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() [[:alnum:]] (character class in range)",
+ fn(): void {
+ assert(
+ match(
+ "[[:alnum:]]/bar.txt",
+ "a/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ match(
+ "[[:alnum:]abc]/bar.txt",
+ "1/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ match(
+ "[[:digit:]]/bar.txt",
+ "1/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ match(
+ "[[:digit:]b]/bar.txt",
+ "b/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ match(
+ "[![:digit:]b]/bar.txt",
+ "a/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "[[:alnum:]]/bar.txt",
+ "!/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "[[:digit:]]/bar.txt",
+ "a/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "[[:digit:]b]/bar.txt",
+ "a/bar.txt",
+ { extended: false, globstar: false },
+ ),
+ );
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() {} (brace expansion)",
+ fn(): void {
+ assert(
+ match("foo{bar,baaz}", "foobaaz", { extended: false, globstar: false }),
+ );
+ assert(
+ match("foo{bar,baaz}", "foobar", { extended: false, globstar: false }),
+ );
+ assert(
+ !match("foo{bar,baaz}", "foobuzz", { extended: false, globstar: false }),
+ );
+ assert(
+ match("foo{bar,b*z}", "foobuzz", { extended: false, globstar: false }),
+ );
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Complex matches",
+ fn(): void {
+ assert(
+ match(
+ "http://?o[oz].b*z.com/{*.js,*.html}",
+ "http://foo.baaz.com/jquery.min.js",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ match(
+ "http://?o[oz].b*z.com/{*.js,*.html}",
+ "http://moz.buzz.com/index.html",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "http://?o[oz].b*z.com/{*.js,*.html}",
+ "http://moz.buzz.com/index.htm",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "http://?o[oz].b*z.com/{*.js,*.html}",
+ "http://moz.bar.com/index.html",
+ { extended: false, globstar: false },
+ ),
+ );
+ assert(
+ !match(
+ "http://?o[oz].b*z.com/{*.js,*.html}",
+ "http://flozz.buzz.com/index.html",
+ { extended: false, globstar: false },
+ ),
+ );
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() ** (globstar)",
+ fn(): void {
+ assert(match("/foo/**", "/foo/bar.txt"));
+ assert(match("/foo/**", "/foo/bar/baz.txt"));
+ assert(!match("/foo/**", "/foo/bar/baz.txt", { globstar: false }));
+ assert(match("/foo/**", "/foo/bar", { globstar: false }));
+ assert(match("/foo/**/*.txt", "/foo/bar/baz.txt"));
+ assert(match("/foo/**/*.txt", "/foo/bar/baz/qux.txt"));
+ assert(match("/foo/**/bar.txt", "/foo/bar.txt"));
+ assert(match("/foo/**/**/bar.txt", "/foo/bar.txt"));
+ assert(match("/foo/**/*/baz.txt", "/foo/bar/baz.txt"));
+ assert(match("/foo/**/*.txt", "/foo/bar.txt"));
+ assert(match("/foo/**/**/*.txt", "/foo/bar.txt"));
+ assert(match("/foo/**/*/*.txt", "/foo/bar/baz.txt"));
+ assert(match("**/*.txt", "/foo/bar/baz/qux.txt"));
+ assert(match("**/foo.txt", "foo.txt"));
+ assert(match("**/*.txt", "foo.txt"));
+ assert(!match("/foo/**.txt", "/foo/bar/baz/qux.txt"));
+ assert(
+ !match("/foo/bar**/*.txt", "/foo/bar/baz/qux.txt"),
+ );
+ assert(!match("/foo/bar**", "/foo/bar/baz.txt"));
+ assert(!match("**/.txt", "/foo/bar/baz/qux.txt"));
+ assert(
+ !match(
+ "http://foo.com/*",
+ "http://foo.com/bar/baz/jquery.min.js",
+ ),
+ );
+ assert(
+ !match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js"),
+ );
+ assert(
+ match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js"),
+ );
+ assert(
+ match(
+ "http://foo.com/**/jquery.min.js",
+ "http://foo.com/bar/baz/jquery.min.js",
+ ),
+ );
+ assert(
+ !match(
+ "http://foo.com/*/jquery.min.js",
+ "http://foo.com/bar/baz/jquery.min.js",
+ ),
+ );
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() ?(pattern-list) (extended: match zero or one)",
+ fn(): void {
+ assert(match("?(foo).txt", "foo.txt"));
+ assert(!match("?(foo).txt", "foo.txt", { extended: false }));
+ assert(match("?(foo).txt", "a(foo).txt", { extended: false }));
+ assert(match("?(foo).txt", ".txt"));
+ assert(match("?(foo|bar)baz.txt", "foobaz.txt"));
+ assert(match("?(ba[zr]|qux)baz.txt", "bazbaz.txt"));
+ assert(match("?(ba[zr]|qux)baz.txt", "barbaz.txt"));
+ assert(match("?(ba[zr]|qux)baz.txt", "quxbaz.txt"));
+ assert(match("?(ba[!zr]|qux)baz.txt", "batbaz.txt"));
+ assert(match("?(ba*|qux)baz.txt", "batbaz.txt"));
+ assert(match("?(ba*|qux)baz.txt", "batttbaz.txt"));
+ assert(match("?(ba*|qux)baz.txt", "quxbaz.txt"));
+ assert(match("?(ba?(z|r)|qux)baz.txt", "bazbaz.txt"));
+ assert(match("?(ba?(z|?(r))|qux)baz.txt", "bazbaz.txt"));
+ assert(!match("?(foo|bar)baz.txt", "foobarbaz.txt"));
+ assert(!match("?(ba[zr]|qux)baz.txt", "bazquxbaz.txt"));
+ assert(!match("?(ba[!zr]|qux)baz.txt", "bazbaz.txt"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() *(pattern-list) (extended: match zero or more)",
+ fn(): void {
+ assert(match("*(foo).txt", "foo.txt"));
+ assert(!match("*(foo).txt", "foo.txt", { extended: false }));
+ assert(match("*(foo).txt", "bar(foo).txt", { extended: false }));
+ assert(match("*(foo).txt", "foofoo.txt"));
+ assert(match("*(foo).txt", ".txt"));
+ assert(match("*(fooo).txt", ".txt"));
+ assert(!match("*(fooo).txt", "foo.txt"));
+ assert(match("*(foo|bar).txt", "foobar.txt"));
+ assert(match("*(foo|bar).txt", "barbar.txt"));
+ assert(match("*(foo|bar).txt", "barfoobar.txt"));
+ assert(match("*(foo|bar).txt", ".txt"));
+ assert(match("*(foo|ba[rt]).txt", "bat.txt"));
+ assert(match("*(foo|b*[rt]).txt", "blat.txt"));
+ assert(!match("*(foo|b*[rt]).txt", "tlat.txt"));
+ assert(match("*(*).txt", "whatever.txt"));
+ assert(match("*(foo|bar)/**/*.txt", "foo/hello/world/bar.txt"));
+ assert(match("*(foo|bar)/**/*.txt", "foo/world/bar.txt"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() +(pattern-list) (extended: match 1 or more)",
+ fn(): void {
+ assert(match("+(foo).txt", "foo.txt"));
+ assert(!match("+(foo).txt", "foo.txt", { extended: false }));
+ assert(match("+(foo).txt", "+(foo).txt", { extended: false }));
+ assert(!match("+(foo).txt", ".txt"));
+ assert(match("+(foo|bar).txt", "foobar.txt"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() @(pattern-list) (extended: match one)",
+ fn(): void {
+ assert(match("@(foo).txt", "foo.txt"));
+ assert(!match("@(foo).txt", "foo.txt", { extended: false }));
+ assert(match("@(foo).txt", "@(foo).txt", { extended: false }));
+ assert(match("@(foo|baz)bar.txt", "foobar.txt"));
+ assert(!match("@(foo|baz)bar.txt", "foobazbar.txt"));
+ assert(!match("@(foo|baz)bar.txt", "foofoobar.txt"));
+ assert(!match("@(foo|baz)bar.txt", "toofoobar.txt"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() !(pattern-list) (extended: match any except)",
+ fn(): void {
+ assert(match("!(boo).txt", "foo.txt"));
+ assert(!match("!(boo).txt", "foo.txt", { extended: false }));
+ assert(match("!(boo).txt", "!(boo).txt", { extended: false }));
+ assert(match("!(foo|baz)bar.txt", "buzbar.txt"));
+ assert(match("!({foo,bar})baz.txt", "notbaz.txt"));
+ assert(!match("!({foo,bar})baz.txt", "foobaz.txt"));
+ },
+});
+
+Deno.test({
+ name:
+ "[path] globToRegExp() Special extended characters should match themselves",
+ fn(): void {
+ const glob = "\\/$^+.()=!|,.*";
+ assert(match(glob, glob));
+ assert(match(glob, glob, { extended: false }));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Special extended characters in range",
+ fn(): void {
+ assertEquals(globToRegExp("[?*+@!|]", { os: "linux" }), /^[?*+@!|]\/*$/);
+ assertEquals(globToRegExp("[!?*+@!|]", { os: "linux" }), /^[^?*+@!|]\/*$/);
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Special RegExp characters in range",
+ fn(): void {
+ // Excluding characters checked in the previous test.
+ assertEquals(globToRegExp("[\\$^.=]", { os: "linux" }), /^[\\$^.=]\/*$/);
+ assertEquals(globToRegExp("[!\\$^.=]", { os: "linux" }), /^[^\\$^.=]\/*$/);
+ assertEquals(globToRegExp("[^^]", { os: "linux" }), /^[\^^]\/*$/);
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Repeating separators",
+ fn() {
+ assert(match("foo/bar", "foo//bar"));
+ assert(match("foo//bar", "foo/bar"));
+ assert(match("foo//bar", "foo//bar"));
+ assert(match("**/bar", "foo//bar"));
+ assert(match("**//bar", "foo/bar"));
+ assert(match("**//bar", "foo//bar"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Trailing separators",
+ fn() {
+ assert(match("foo", "foo/"));
+ assert(match("foo/", "foo"));
+ assert(match("foo/", "foo/"));
+ assert(match("**", "foo/"));
+ assert(match("**/", "foo"));
+ assert(match("**/", "foo/"));
+ },
+});
+
+Deno.test({
+ name: "[path] globToRegExp() Backslashes on Windows",
+ fn() {
+ assert(match("foo/bar", "foo\\bar", { os: "windows" }));
+ assert(match("foo\\bar", "foo/bar", { os: "windows" }));
+ assert(match("foo\\bar", "foo\\bar", { os: "windows" }));
+ assert(match("**/bar", "foo\\bar", { os: "windows" }));
+ assert(match("**\\bar", "foo/bar", { os: "windows" }));
+ assert(match("**\\bar", "foo\\bar", { os: "windows" }));
+ },
+});
+
+Deno.test({
+ name: "[path] GlobToRegExpOptions::extended",
+ fn() {
+ const pattern1 = globToRegExp("?(foo|bar)");
+ assertEquals("foo".match(pattern1)?.[0], "foo");
+ assertEquals("bar".match(pattern1)?.[0], "bar");
+
+ const pattern2 = globToRegExp("?(foo|bar)", { extended: false });
+ assertEquals("foo".match(pattern2)?.[0], undefined);
+ assertEquals("bar".match(pattern2)?.[0], undefined);
+ assertEquals("?(foo|bar)".match(pattern2)?.[0], "?(foo|bar)");
+ },
+});
+
+Deno.test({
+ name: "[path] GlobToRegExpOptions::globstar",
+ fn() {
+ const pattern1 = globToRegExp("**/foo");
+ assertEquals("foo".match(pattern1)?.[0], "foo");
+ assertEquals("path/to/foo".match(pattern1)?.[0], "path/to/foo");
+
+ const pattern2 = globToRegExp("**/foo", { globstar: false });
+ assertEquals("foo".match(pattern2)?.[0], undefined);
+ assertEquals("path/to/foo".match(pattern2)?.[0], undefined);
+ assertEquals("path-to/foo".match(pattern2)?.[0], "path-to/foo");
+ },
+});
+
+Deno.test({
+ name: "[path] GlobToRegExpOptions::os",
+ fn() {
+ const pattern1 = globToRegExp("foo/bar", { os: "linux" });
+ assertEquals("foo/bar".match(pattern1)?.[0], "foo/bar");
+ assertEquals("foo\\bar".match(pattern1)?.[0], undefined);
+
+ const pattern2 = globToRegExp("foo/bar", { os: "windows" });
+ assertEquals("foo/bar".match(pattern2)?.[0], "foo/bar");
+ assertEquals("foo\\bar".match(pattern2)?.[0], "foo\\bar");
+ },
+});
+
+Deno.test({
+ name: "[path] isGlob()",
fn(): void {
// should be true if valid glob pattern
assert(isGlob("!foo.js"));
@@ -238,10 +564,10 @@ Deno.test({
},
});
-Deno.test("normalizeGlobGlobstar", function (): void {
+Deno.test("[path] normalizeGlob() Globstar", function (): void {
assertEquals(normalizeGlob(`**${SEP}..`, { globstar: true }), `**${SEP}..`);
});
-Deno.test("joinGlobsGlobstar", function (): void {
+Deno.test("[path] joinGlobs() Globstar", function (): void {
assertEquals(joinGlobs(["**", ".."], { globstar: true }), `**${SEP}..`);
});