Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | 26x 301x 277x 24x 301x 70x 77x 44x 26x 738x 738x 5x 5x 5x 150x 147x 3x | /**
* Internal path + pattern utilities shared by the grep and glob
* engines. Not part of the public barrel — implementation details
* factored out to prevent drift between the two engines.
*/
import path from "node:path";
import picomatch from "picomatch";
/**
* A precompiled glob matcher. We cache whether the pattern is
* "path-mode" (tested against the relative path, e.g. `src/*.ts`)
* vs "basename-mode" (tested against just the file's basename, e.g.
* `*.ts`) so the per-file call skips the `pattern.includes("/")` check
* every time.
*
* Matches the init-wizard's fs-fallback heuristic and ripgrep's
* `--glob` semantics: patterns with `/` anchor to the relative path
* from cwd, patterns without `/` match the basename anywhere.
*/
export type CompiledMatcher = {
test: (input: string) => boolean;
pathMode: boolean;
};
/**
* Compile a picomatch matcher for a single glob pattern. `dot: true`
* so dotfiles aren't silently excluded (the walker's own `hidden`
* flag owns that policy; glob patterns should match whatever the
* walker yields).
*/
export function compileMatcher(pattern: string): CompiledMatcher {
return {
test: picomatch(pattern, { dot: true }),
pathMode: pattern.includes("/"),
};
}
/**
* Compile zero or more glob patterns. The input is the shape grep
* and glob both accept on their options objects: `string | readonly
* string[] | undefined`. Undefined returns an empty array so callers
* can short-circuit on `.length === 0` without null-checks.
*/
export function compileMatchers(
patterns: string | readonly string[] | undefined
): CompiledMatcher[] {
if (patterns === undefined) {
return [];
}
const list = typeof patterns === "string" ? [patterns] : patterns;
return list.map(compileMatcher);
}
/**
* True if at least one matcher accepts the given path. The caller
* supplies both the relative path and its basename so we don't
* recompute the basename per matcher.
*
* Path-mode matchers test against `relPath`, basename-mode against
* `basename` — see `CompiledMatcher.pathMode`.
*/
export function matchesAny(
matchers: readonly CompiledMatcher[],
relPath: string,
basename: string
): boolean {
for (const m of matchers) {
if (m.test(m.pathMode ? relPath : basename)) {
return true;
}
}
return false;
}
/**
* Extract the basename (segment after the last `/`) from a
* POSIX-normalized relative path. Equivalent to `path.posix.basename`
* but avoids the import cycle + is slightly cheaper on the hot path.
*/
export function basenameOf(rel: string): string {
const slashIdx = rel.lastIndexOf("/");
return slashIdx === -1 ? rel : rel.slice(slashIdx + 1);
}
/**
* Join two POSIX-style path segments with a single `/` separator,
* trimming a trailing `/` on the left or a leading `/` on the right
* so we never produce a `//` in the middle.
*/
export function joinPosix(a: string, b: string): string {
const left = a.endsWith("/") ? a.slice(0, -1) : a;
const right = b.startsWith("/") ? b.slice(1) : b;
return `${left}/${right}`;
}
/**
* Narrow the walker's root when `opts.path` is set. When `sub` is
* undefined we just pass `cwd` through; otherwise we resolve the
* subpath against `cwd` and hand the walker that as its new root.
*
* ### Sandboxing is the caller's job
*
* `path.resolve` happily resolves an absolute path OR a relative one,
* so a malicious `../../etc` would escape the sandbox. Callers that
* accept user input (init-wizard tool adapters) MUST pre-validate
* via `src/lib/init/tools/shared.ts::safePath` before forwarding to
* grep/glob. The engine explicitly trusts the passed value.
*/
export function walkerRoot(cwd: string, sub: string | undefined): string {
if (!sub) {
return cwd;
}
return path.resolve(cwd, sub);
}
|