summaryrefslogtreecommitdiff
path: root/std/path/_globrex.ts
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2020-05-09 13:34:47 +0100
committerGitHub <noreply@github.com>2020-05-09 08:34:47 -0400
commitf184332c09c851faac50f598d29ebe4426e05464 (patch)
tree2659aba63702537fcde1bb64ddeafea1e5863f3e /std/path/_globrex.ts
parent2b02535028f868ea8dfc471c4921a237747ccd4a (diff)
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
Diffstat (limited to 'std/path/_globrex.ts')
-rw-r--r--std/path/_globrex.ts327
1 files changed, 327 insertions, 0 deletions
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;
+}