1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00
denoland-deno/std/path/glob.ts

124 lines
3.2 KiB
TypeScript

import { SEP, SEP_PATTERN } from "./constants.ts";
import { globrex } from "./globrex.ts";
import { join, normalize } from "./mod.ts";
import { assert } from "../testing/asserts.ts";
export interface GlobOptions {
extended?: boolean;
globstar?: boolean;
}
export interface GlobToRegExpOptions extends GlobOptions {
flags?: string;
}
/**
* Generate a regex based on glob pattern and options
* This was meant to be using the the `fs.walk` function
* but can be used anywhere else.
* Examples:
*
* Looking for all the `ts` files:
* walkSync(".", {
* match: [globToRegExp("*.ts")]
* })
*
* 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
*/
export function globToRegExp(
glob: string,
{ extended = false, globstar = true }: GlobToRegExpOptions = {}
): RegExp {
const result = globrex(glob, {
extended,
globstar,
strict: false,
filepath: true,
});
assert(result.path != null);
return result.path.regex;
}
/** Test whether the given string is a glob */
export function isGlob(str: string): boolean {
const chars: Record<string, string> = { "{": "}", "(": ")", "[": "]" };
/* eslint-disable-next-line max-len */
const regex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
if (str === "") {
return false;
}
let match: RegExpExecArray | null;
while ((match = regex.exec(str))) {
if (match[2]) return true;
let idx = match.index + match[0].length;
// if an open bracket/brace/paren is escaped,
// set the index to the next closing character
const open = match[1];
const close = open ? chars[open] : null;
if (open && close) {
const n = str.indexOf(close, idx);
if (n !== -1) {
idx = n + 1;
}
}
str = str.slice(idx);
}
return false;
}
/** Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. */
export function normalizeGlob(
glob: string,
{ globstar = false }: GlobOptions = {}
): string {
if (!!glob.match(/\0/g)) {
throw new Error(`Glob contains invalid characters: "${glob}"`);
}
if (!globstar) {
return normalize(glob);
}
const s = SEP_PATTERN.source;
const badParentPattern = new RegExp(
`(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`,
"g"
);
return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, "..");
}
/** Like join(), but doesn't collapse "**\/.." when `globstar` is true. */
export function joinGlobs(
globs: string[],
{ extended = false, globstar = false }: GlobOptions = {}
): string {
if (!globstar || globs.length == 0) {
return join(...globs);
}
if (globs.length === 0) return ".";
let joined: string | undefined;
for (const glob of globs) {
const path = glob;
if (path.length > 0) {
if (!joined) joined = path;
else joined += `${SEP}${path}`;
}
}
if (!joined) return ".";
return normalizeGlob(joined, { extended, globstar });
}