From f184332c09c851faac50f598d29ebe4426e05464 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Sat, 9 May 2020 13:34:47 +0100 Subject: BREAKING(std): reorganization (#5087) * Prepend underscores to private modules * Remove collectUint8Arrays() It would be a misuse of Deno.iter()'s result. * Move std/_util/async.ts to std/async * Move std/util/sha*.ts to std/hash --- std/path/_constants.ts | 54 +++ std/path/_globrex.ts | 327 ++++++++++++++++++ std/path/_globrex_test.ts | 827 ++++++++++++++++++++++++++++++++++++++++++++++ std/path/_util.ts | 116 +++++++ std/path/common.ts | 2 +- std/path/constants.ts | 54 --- std/path/glob.ts | 4 +- std/path/globrex.ts | 327 ------------------ std/path/globrex_test.ts | 827 ---------------------------------------------- std/path/mod.ts | 7 +- std/path/posix.ts | 4 +- std/path/separator.ts | 4 + std/path/utils.ts | 116 ------- std/path/win32.ts | 4 +- 14 files changed, 1338 insertions(+), 1335 deletions(-) create mode 100644 std/path/_constants.ts create mode 100644 std/path/_globrex.ts create mode 100644 std/path/_globrex_test.ts create mode 100644 std/path/_util.ts delete mode 100644 std/path/constants.ts delete mode 100644 std/path/globrex.ts delete mode 100644 std/path/globrex_test.ts create mode 100644 std/path/separator.ts delete mode 100644 std/path/utils.ts (limited to 'std/path') diff --git a/std/path/_constants.ts b/std/path/_constants.ts new file mode 100644 index 000000000..ae0aac184 --- /dev/null +++ b/std/path/_constants.ts @@ -0,0 +1,54 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ + +const { build } = Deno; + +// Alphabet chars. +export const CHAR_UPPERCASE_A = 65; /* A */ +export const CHAR_LOWERCASE_A = 97; /* a */ +export const CHAR_UPPERCASE_Z = 90; /* Z */ +export const CHAR_LOWERCASE_Z = 122; /* z */ + +// Non-alphabetic chars. +export const CHAR_DOT = 46; /* . */ +export const CHAR_FORWARD_SLASH = 47; /* / */ +export const CHAR_BACKWARD_SLASH = 92; /* \ */ +export const CHAR_VERTICAL_LINE = 124; /* | */ +export const CHAR_COLON = 58; /* : */ +export const CHAR_QUESTION_MARK = 63; /* ? */ +export const CHAR_UNDERSCORE = 95; /* _ */ +export const CHAR_LINE_FEED = 10; /* \n */ +export const CHAR_CARRIAGE_RETURN = 13; /* \r */ +export const CHAR_TAB = 9; /* \t */ +export const CHAR_FORM_FEED = 12; /* \f */ +export const CHAR_EXCLAMATION_MARK = 33; /* ! */ +export const CHAR_HASH = 35; /* # */ +export const CHAR_SPACE = 32; /* */ +export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ +export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ +export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ +export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ +export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ +export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ +export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ +export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ +export const CHAR_HYPHEN_MINUS = 45; /* - */ +export const CHAR_PLUS = 43; /* + */ +export const CHAR_DOUBLE_QUOTE = 34; /* " */ +export const CHAR_SINGLE_QUOTE = 39; /* ' */ +export const CHAR_PERCENT = 37; /* % */ +export const CHAR_SEMICOLON = 59; /* ; */ +export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ +export const CHAR_GRAVE_ACCENT = 96; /* ` */ +export const CHAR_AT = 64; /* @ */ +export const CHAR_AMPERSAND = 38; /* & */ +export const CHAR_EQUAL = 61; /* = */ + +// Digits +export const CHAR_0 = 48; /* 0 */ +export const CHAR_9 = 57; /* 9 */ + +const isWindows = build.os == "windows"; + +export const SEP = isWindows ? "\\" : "/"; +export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/path/_globrex.ts b/std/path/_globrex.ts new file mode 100644 index 000000000..3fc69dd6c --- /dev/null +++ b/std/path/_globrex.ts @@ -0,0 +1,327 @@ +// This file is ported from globrex@0.1.2 +// MIT License +// Copyright (c) 2018 Terkel Gjervig Nielsen + +const isWin = Deno.build.os === "windows"; +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 equivelant + * 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 new file mode 100644 index 000000000..2974b4719 --- /dev/null +++ b/std/path/_globrex_test.ts @@ -0,0 +1,827 @@ +// This file is ported from globrex@0.1.2 +// MIT License +// Copyright (c) 2018 Terkel Gjervig Nielsen + +const { test } = Deno; +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; +} + +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"); + }, +}); + +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" + ); + }, +}); + +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 + ); + }, +}); + +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); + }, +}); + +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); + }, +}); + +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 + ); + }, +}); + +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); + }, +}); + +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); + }, +}); + +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); + }, +}); + +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); + }, +}); + +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 + ); + }, +}); + +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 + ); + }, +}); + +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 + ); + }, +}); + +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); + }, +}); + +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 + ); + }, +}); + +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 + ); + }, +}); + +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); + }, +}); + +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/_util.ts b/std/path/_util.ts new file mode 100644 index 000000000..2776303cb --- /dev/null +++ b/std/path/_util.ts @@ -0,0 +1,116 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ + +import { FormatInputPathObject } from "./interface.ts"; +import { + CHAR_UPPERCASE_A, + CHAR_LOWERCASE_A, + CHAR_UPPERCASE_Z, + CHAR_LOWERCASE_Z, + CHAR_DOT, + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, +} from "./_constants.ts"; + +export function assertPath(path: string): void { + if (typeof path !== "string") { + throw new TypeError( + `Path must be a string. Received ${JSON.stringify(path)}` + ); + } +} + +export function isPosixPathSeparator(code: number): boolean { + return code === CHAR_FORWARD_SLASH; +} + +export function isPathSeparator(code: number): boolean { + return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; +} + +export function isWindowsDeviceRoot(code: number): boolean { + return ( + (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || + (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) + ); +} + +// Resolves . and .. elements in a path with directory names +export function normalizeString( + path: string, + allowAboveRoot: boolean, + separator: string, + isPathSeparator: (code: number) => boolean +): string { + let res = ""; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code: number | undefined; + for (let i = 0, len = path.length; i <= len; ++i) { + if (i < len) code = path.charCodeAt(i); + else if (isPathSeparator(code!)) break; + else code = CHAR_FORWARD_SLASH; + + if (isPathSeparator(code!)) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } else if (lastSlash !== i - 1 && dots === 2) { + if ( + res.length < 2 || + lastSegmentLength !== 2 || + res.charCodeAt(res.length - 1) !== CHAR_DOT || + res.charCodeAt(res.length - 2) !== CHAR_DOT + ) { + if (res.length > 2) { + const lastSlashIndex = res.lastIndexOf(separator); + if (lastSlashIndex === -1) { + res = ""; + lastSegmentLength = 0; + } else { + res = res.slice(0, lastSlashIndex); + lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); + } + lastSlash = i; + dots = 0; + continue; + } else if (res.length === 2 || res.length === 1) { + res = ""; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + if (res.length > 0) res += `${separator}..`; + else res = ".."; + lastSegmentLength = 2; + } + } else { + if (res.length > 0) res += separator + path.slice(lastSlash + 1, i); + else res = path.slice(lastSlash + 1, i); + lastSegmentLength = i - lastSlash - 1; + } + lastSlash = i; + dots = 0; + } else if (code === CHAR_DOT && dots !== -1) { + ++dots; + } else { + dots = -1; + } + } + return res; +} + +export function _format( + sep: string, + pathObject: FormatInputPathObject +): string { + const dir: string | undefined = pathObject.dir || pathObject.root; + const base: string = + pathObject.base || (pathObject.name || "") + (pathObject.ext || ""); + if (!dir) return base; + if (dir === pathObject.root) return dir + base; + return dir + sep + base; +} diff --git a/std/path/common.ts b/std/path/common.ts index 0a4de3f0c..e0e51ef23 100644 --- a/std/path/common.ts +++ b/std/path/common.ts @@ -1,6 +1,6 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { SEP } from "./constants.ts"; +import { SEP } from "./separator.ts"; /** Determines the common path from a set of paths, using an optional separator, * which defaults to the OS default separator. diff --git a/std/path/constants.ts b/std/path/constants.ts deleted file mode 100644 index 97d3bcf58..000000000 --- a/std/path/constants.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -const { build } = Deno; - -// Alphabet chars. -export const CHAR_UPPERCASE_A = 65; /* A */ -export const CHAR_LOWERCASE_A = 97; /* a */ -export const CHAR_UPPERCASE_Z = 90; /* Z */ -export const CHAR_LOWERCASE_Z = 122; /* z */ - -// Non-alphabetic chars. -export const CHAR_DOT = 46; /* . */ -export const CHAR_FORWARD_SLASH = 47; /* / */ -export const CHAR_BACKWARD_SLASH = 92; /* \ */ -export const CHAR_VERTICAL_LINE = 124; /* | */ -export const CHAR_COLON = 58; /* : */ -export const CHAR_QUESTION_MARK = 63; /* ? */ -export const CHAR_UNDERSCORE = 95; /* _ */ -export const CHAR_LINE_FEED = 10; /* \n */ -export const CHAR_CARRIAGE_RETURN = 13; /* \r */ -export const CHAR_TAB = 9; /* \t */ -export const CHAR_FORM_FEED = 12; /* \f */ -export const CHAR_EXCLAMATION_MARK = 33; /* ! */ -export const CHAR_HASH = 35; /* # */ -export const CHAR_SPACE = 32; /* */ -export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ -export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ -export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ -export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ -export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ -export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ -export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ -export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ -export const CHAR_HYPHEN_MINUS = 45; /* - */ -export const CHAR_PLUS = 43; /* + */ -export const CHAR_DOUBLE_QUOTE = 34; /* " */ -export const CHAR_SINGLE_QUOTE = 39; /* ' */ -export const CHAR_PERCENT = 37; /* % */ -export const CHAR_SEMICOLON = 59; /* ; */ -export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ -export const CHAR_GRAVE_ACCENT = 96; /* ` */ -export const CHAR_AT = 64; /* @ */ -export const CHAR_AMPERSAND = 38; /* & */ -export const CHAR_EQUAL = 61; /* = */ - -// Digits -export const CHAR_0 = 48; /* 0 */ -export const CHAR_9 = 57; /* 9 */ - -export const isWindows = build.os === "windows"; -export const EOL = isWindows ? "\r\n" : "\n"; -export const SEP = isWindows ? "\\" : "/"; -export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/path/glob.ts b/std/path/glob.ts index a11865c26..80672579d 100644 --- a/std/path/glob.ts +++ b/std/path/glob.ts @@ -1,5 +1,5 @@ -import { SEP, SEP_PATTERN } from "./constants.ts"; -import { globrex } from "./globrex.ts"; +import { SEP, SEP_PATTERN } from "./separator.ts"; +import { globrex } from "./_globrex.ts"; import { join, normalize } from "./mod.ts"; import { assert } from "../testing/asserts.ts"; diff --git a/std/path/globrex.ts b/std/path/globrex.ts deleted file mode 100644 index 3fc69dd6c..000000000 --- a/std/path/globrex.ts +++ /dev/null @@ -1,327 +0,0 @@ -// This file is ported from globrex@0.1.2 -// MIT License -// Copyright (c) 2018 Terkel Gjervig Nielsen - -const isWin = Deno.build.os === "windows"; -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 equivelant - * 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 c52ed108e..000000000 --- a/std/path/globrex_test.ts +++ /dev/null @@ -1,827 +0,0 @@ -// This file is ported from globrex@0.1.2 -// MIT License -// Copyright (c) 2018 Terkel Gjervig Nielsen - -const { test } = Deno; -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; -} - -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"); - }, -}); - -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" - ); - }, -}); - -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 - ); - }, -}); - -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); - }, -}); - -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); - }, -}); - -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 - ); - }, -}); - -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); - }, -}); - -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); - }, -}); - -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); - }, -}); - -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); - }, -}); - -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 - ); - }, -}); - -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 - ); - }, -}); - -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 - ); - }, -}); - -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); - }, -}); - -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 - ); - }, -}); - -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 - ); - }, -}); - -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); - }, -}); - -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/mod.ts b/std/path/mod.ts index 104e0b616..9cb7f1edb 100644 --- a/std/path/mod.ts +++ b/std/path/mod.ts @@ -4,7 +4,7 @@ import * as _win32 from "./win32.ts"; import * as _posix from "./posix.ts"; -import { isWindows } from "./constants.ts"; +const isWindows = Deno.build.os == "windows"; const path = isWindows ? _win32 : _posix; @@ -27,8 +27,7 @@ export const { toNamespacedPath, } = path; -export { common } from "./common.ts"; -export { EOL, SEP, SEP_PATTERN, isWindows } from "./constants.ts"; +export * from "./common.ts"; +export { SEP, SEP_PATTERN } from "./separator.ts"; export * from "./interface.ts"; export * from "./glob.ts"; -export * from "./globrex.ts"; diff --git a/std/path/posix.ts b/std/path/posix.ts index 156aba796..e88eb3f97 100644 --- a/std/path/posix.ts +++ b/std/path/posix.ts @@ -3,14 +3,14 @@ const { cwd } = Deno; import { FormatInputPathObject, ParsedPath } from "./interface.ts"; -import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./constants.ts"; +import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./_constants.ts"; import { assertPath, normalizeString, isPosixPathSeparator, _format, -} from "./utils.ts"; +} from "./_util.ts"; export const sep = "/"; export const delimiter = ":"; diff --git a/std/path/separator.ts b/std/path/separator.ts new file mode 100644 index 000000000..fb990b808 --- /dev/null +++ b/std/path/separator.ts @@ -0,0 +1,4 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +const isWindows = Deno.build.os == "windows"; +export const SEP = isWindows ? "\\" : "/"; +export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/path/utils.ts b/std/path/utils.ts deleted file mode 100644 index fc3dc5be9..000000000 --- a/std/path/utils.ts +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ - -import { FormatInputPathObject } from "./interface.ts"; -import { - CHAR_UPPERCASE_A, - CHAR_LOWERCASE_A, - CHAR_UPPERCASE_Z, - CHAR_LOWERCASE_Z, - CHAR_DOT, - CHAR_FORWARD_SLASH, - CHAR_BACKWARD_SLASH, -} from "./constants.ts"; - -export function assertPath(path: string): void { - if (typeof path !== "string") { - throw new TypeError( - `Path must be a string. Received ${JSON.stringify(path)}` - ); - } -} - -export function isPosixPathSeparator(code: number): boolean { - return code === CHAR_FORWARD_SLASH; -} - -export function isPathSeparator(code: number): boolean { - return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; -} - -export function isWindowsDeviceRoot(code: number): boolean { - return ( - (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || - (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) - ); -} - -// Resolves . and .. elements in a path with directory names -export function normalizeString( - path: string, - allowAboveRoot: boolean, - separator: string, - isPathSeparator: (code: number) => boolean -): string { - let res = ""; - let lastSegmentLength = 0; - let lastSlash = -1; - let dots = 0; - let code: number | undefined; - for (let i = 0, len = path.length; i <= len; ++i) { - if (i < len) code = path.charCodeAt(i); - else if (isPathSeparator(code!)) break; - else code = CHAR_FORWARD_SLASH; - - if (isPathSeparator(code!)) { - if (lastSlash === i - 1 || dots === 1) { - // NOOP - } else if (lastSlash !== i - 1 && dots === 2) { - if ( - res.length < 2 || - lastSegmentLength !== 2 || - res.charCodeAt(res.length - 1) !== CHAR_DOT || - res.charCodeAt(res.length - 2) !== CHAR_DOT - ) { - if (res.length > 2) { - const lastSlashIndex = res.lastIndexOf(separator); - if (lastSlashIndex === -1) { - res = ""; - lastSegmentLength = 0; - } else { - res = res.slice(0, lastSlashIndex); - lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); - } - lastSlash = i; - dots = 0; - continue; - } else if (res.length === 2 || res.length === 1) { - res = ""; - lastSegmentLength = 0; - lastSlash = i; - dots = 0; - continue; - } - } - if (allowAboveRoot) { - if (res.length > 0) res += `${separator}..`; - else res = ".."; - lastSegmentLength = 2; - } - } else { - if (res.length > 0) res += separator + path.slice(lastSlash + 1, i); - else res = path.slice(lastSlash + 1, i); - lastSegmentLength = i - lastSlash - 1; - } - lastSlash = i; - dots = 0; - } else if (code === CHAR_DOT && dots !== -1) { - ++dots; - } else { - dots = -1; - } - } - return res; -} - -export function _format( - sep: string, - pathObject: FormatInputPathObject -): string { - const dir: string | undefined = pathObject.dir || pathObject.root; - const base: string = - pathObject.base || (pathObject.name || "") + (pathObject.ext || ""); - if (!dir) return base; - if (dir === pathObject.root) return dir + base; - return dir + sep + base; -} diff --git a/std/path/win32.ts b/std/path/win32.ts index 401e572db..0557b3768 100644 --- a/std/path/win32.ts +++ b/std/path/win32.ts @@ -8,7 +8,7 @@ import { CHAR_BACKWARD_SLASH, CHAR_COLON, CHAR_QUESTION_MARK, -} from "./constants.ts"; +} from "./_constants.ts"; import { assertPath, @@ -16,7 +16,7 @@ import { isWindowsDeviceRoot, normalizeString, _format, -} from "./utils.ts"; +} from "./_util.ts"; import { assert } from "../testing/asserts.ts"; export const sep = "\\"; -- cgit v1.2.3