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 { extended?: boolean; globstar?: boolean; strict?: boolean; filepath?: boolean; flags?: string;}
export interface GlobrexResult { regex: RegExp; path?: { regex: RegExp; segments: RegExp[]; globstar?: RegExp; };}
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 = [];
let inGroup = false; let inRange = false;
const ext = [];
interface AddOptions { split?: boolean; last?: boolean; only?: string; }
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 !== "") { 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++; 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++; 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; } const prevChar = glob[i - 1]; let starCount = 1; while (glob[i + 1] === "*") { starCount++; i++; } const nextChar = glob[i + 1]; if (!globstar) { add(".*"); } else { const isGlobstar = starCount > 1 && [SEP_RAW, "/", undefined].includes(prevChar) && [SEP_RAW, "/", undefined].includes(nextChar); if (isGlobstar) { add(GLOBSTAR, { only: "regex" }); add(GLOBSTAR_SEGMENT, { only: "path", last: true, split: true }); i++; } else { add(WILDCARD, { only: "regex" }); add(WILDCARD_SEGMENT, { only: "path" }); } } continue; }
add(c); }
if (!flags.includes("g")) { regex = `^${regex}$`; segment = `^${segment}$`; if (filepath) pathRegexStr = `^${pathRegexStr}$`; }
const result: GlobrexResult = { regex: new RegExp(regex, flags) };
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;}