mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
refactor: Rewrite globToRegExp() (#6963)
This commit is contained in:
parent
f6cd36f8c8
commit
24590b012f
6 changed files with 682 additions and 1309 deletions
|
@ -18,7 +18,7 @@ import { assert } from "../_util/assert.ts";
|
|||
|
||||
const isWindows = Deno.build.os == "windows";
|
||||
|
||||
export interface ExpandGlobOptions extends GlobOptions {
|
||||
export interface ExpandGlobOptions extends Omit<GlobOptions, "os"> {
|
||||
root?: string;
|
||||
exclude?: string[];
|
||||
includeDirs?: boolean;
|
||||
|
|
|
@ -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 { isWindows };
|
||||
export const isWindows = NATIVE_OS == "windows";
|
||||
|
||||
export { NATIVE_OS };
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
});
|
274
std/path/glob.ts
274
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:
|
||||
/** Convert a glob string to a regular expressions.
|
||||
*
|
||||
* Looking for all the `ts` files:
|
||||
* walkSync(".", {
|
||||
* match: [globToRegExp("*.ts")]
|
||||
* })
|
||||
* // Looking for all the `ts` files:
|
||||
* walkSync(".", {
|
||||
* match: [globToRegExp("*.ts")]
|
||||
* });
|
||||
*
|
||||
* Looking for all the `.json` files in any subfolder:
|
||||
* walkSync(".", {
|
||||
* match: [globToRegExp(join("a", "**", "*.json"),{
|
||||
* flags: "g",
|
||||
* extended: true,
|
||||
* globstar: true
|
||||
* })]
|
||||
* })
|
||||
*
|
||||
* @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 */
|
||||
|
|
|
@ -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";
|
||||
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: "glob: glob to regex",
|
||||
name: "[path] globToRegExp() Basic RegExp",
|
||||
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"),
|
||||
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 },
|
||||
),
|
||||
true,
|
||||
);
|
||||
assertEquals(
|
||||
globToRegExp(join("unicorn", "**", "cathedral.ts")).test(
|
||||
join("unicorn", "in", "the", "kitchen.ts"),
|
||||
assert(
|
||||
match(
|
||||
"[[:alnum:]abc]/bar.txt",
|
||||
"1/bar.txt",
|
||||
{ extended: false, globstar: false },
|
||||
),
|
||||
false,
|
||||
);
|
||||
assertEquals(
|
||||
globToRegExp(join("unicorn", "**", "bathroom.*")).test(
|
||||
join("unicorn", "sleeping", "in", "bathroom.py"),
|
||||
assert(
|
||||
match(
|
||||
"[[:digit:]]/bar.txt",
|
||||
"1/bar.txt",
|
||||
{ extended: false, globstar: false },
|
||||
),
|
||||
true,
|
||||
);
|
||||
assertEquals(
|
||||
globToRegExp(join("unicorn", "!(sleeping)", "bathroom.ts"), {
|
||||
extended: true,
|
||||
}).test(join("unicorn", "flying", "bathroom.ts")),
|
||||
true,
|
||||
assert(
|
||||
match(
|
||||
"[[:digit:]b]/bar.txt",
|
||||
"b/bar.txt",
|
||||
{ extended: false, globstar: false },
|
||||
),
|
||||
);
|
||||
assertEquals(
|
||||
globToRegExp(join("unicorn", "(!sleeping)", "bathroom.ts"), {
|
||||
extended: true,
|
||||
}).test(join("unicorn", "sleeping", "bathroom.ts")),
|
||||
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 },
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
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");
|
||||
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 }),
|
||||
);
|
||||
},
|
||||
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",
|
||||
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}..`);
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue