@edxeth/pi-fff 0.7.2-edxeth.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -0
- package/package.json +54 -0
- package/src/index.ts +1281 -0
- package/src/query.ts +87 -0
package/src/query.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function normalizePathConstraint(
|
|
4
|
+
pathConstraint: string,
|
|
5
|
+
cwd = process.cwd(),
|
|
6
|
+
): string | null {
|
|
7
|
+
let trimmed = pathConstraint.trim();
|
|
8
|
+
if (!trimmed) return trimmed;
|
|
9
|
+
|
|
10
|
+
if (path.isAbsolute(trimmed)) {
|
|
11
|
+
const relative = path.relative(cwd, trimmed).replaceAll(path.sep, "/");
|
|
12
|
+
if (relative === "") return null;
|
|
13
|
+
if (relative.startsWith("../") || relative === ".." || path.isAbsolute(relative)) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`Path constraint must be relative to the workspace: ${pathConstraint}`,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
trimmed = relative;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (trimmed === "." || trimmed === "./") return null;
|
|
22
|
+
// Strip a leading `./` so `./**/*.rs` and `**/*.rs` behave identically.
|
|
23
|
+
if (trimmed.startsWith("./")) trimmed = trimmed.slice(2);
|
|
24
|
+
|
|
25
|
+
// FFF's glob matcher can treat a hidden directory root glob such as
|
|
26
|
+
// `.agents/**` as empty, while the tool contract says this means "inside
|
|
27
|
+
// this directory". Collapse simple trailing recursive directory globs to the
|
|
28
|
+
// directory-prefix constraint understood by the parser. Keep real file globs
|
|
29
|
+
// such as `src/**/*.ts` unchanged.
|
|
30
|
+
const recursiveDir = trimmed.match(/^(.*)\/\*\*(?:\/\*)?$/);
|
|
31
|
+
if (recursiveDir) {
|
|
32
|
+
const dir = recursiveDir[1];
|
|
33
|
+
if (dir && !/[*?[{]/.test(dir)) return `${dir}/`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Already signals path-constraint syntax to the parser.
|
|
37
|
+
if (trimmed.startsWith("/") || trimmed.endsWith("/")) return trimmed;
|
|
38
|
+
// Globs (`*.ts`, `src/**/*.cc`, `{src,lib}`) are handled by the parser.
|
|
39
|
+
if (/[*?[{]/.test(trimmed)) return trimmed;
|
|
40
|
+
// Filename with extension (`main.rs`, `config.json`) → FilePath constraint.
|
|
41
|
+
const lastSegment = trimmed.split("/").pop() ?? "";
|
|
42
|
+
if (/\.[a-zA-Z][a-zA-Z0-9]{0,9}$/.test(lastSegment)) return trimmed;
|
|
43
|
+
// Bare directory prefix → append `/` so the parser sees a PathSegment.
|
|
44
|
+
return `${trimmed}/`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Exclusions are emitted as `!<constraint>` tokens, which the Rust parser
|
|
48
|
+
// understands (crates/fff-query-parser/src/parser.rs). We normalize each one
|
|
49
|
+
// the same way as the include path so bare dirs become PathSegment excludes.
|
|
50
|
+
// Tolerate callers passing already-negated forms like `!src/` by stripping
|
|
51
|
+
// the leading `!` before normalizing so we never double-negate (`!!src/`).
|
|
52
|
+
export function normalizeExcludes(
|
|
53
|
+
exclude: string | string[] | undefined,
|
|
54
|
+
cwd = process.cwd(),
|
|
55
|
+
): string[] {
|
|
56
|
+
if (!exclude) return [];
|
|
57
|
+
const list = Array.isArray(exclude) ? exclude : [exclude];
|
|
58
|
+
const out: string[] = [];
|
|
59
|
+
for (const raw of list) {
|
|
60
|
+
const parts = raw
|
|
61
|
+
.split(/[,\s]+/)
|
|
62
|
+
.map((s) => s.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
for (const p of parts) {
|
|
65
|
+
const stripped = p.startsWith("!") ? p.slice(1) : p;
|
|
66
|
+
const normalized = normalizePathConstraint(stripped, cwd);
|
|
67
|
+
if (normalized) out.push(`!${normalized}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildQuery(
|
|
74
|
+
path: string | undefined,
|
|
75
|
+
pattern: string,
|
|
76
|
+
exclude?: string | string[],
|
|
77
|
+
cwd = process.cwd(),
|
|
78
|
+
): string {
|
|
79
|
+
const parts: string[] = [];
|
|
80
|
+
if (path) {
|
|
81
|
+
const pathConstraint = normalizePathConstraint(path, cwd);
|
|
82
|
+
if (pathConstraint) parts.push(pathConstraint);
|
|
83
|
+
}
|
|
84
|
+
parts.push(...normalizeExcludes(exclude, cwd));
|
|
85
|
+
parts.push(pattern);
|
|
86
|
+
return parts.join(" ");
|
|
87
|
+
}
|