@nanonets/graft 0.8.2 → 0.9.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 +74 -32
- package/dist/ai/llm/openai.d.ts +11 -0
- package/dist/ai/llm/openai.d.ts.map +1 -1
- package/dist/ai/llm/openai.js +33 -1
- package/dist/ai/llm/openai.js.map +1 -1
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +22 -15
- package/dist/ask/ask.js.map +1 -1
- package/dist/ask/fuse.d.ts +4 -0
- package/dist/ask/fuse.d.ts.map +1 -1
- package/dist/ask/fuse.js +2 -1
- package/dist/ask/fuse.js.map +1 -1
- package/dist/ask/index-file.d.ts +1 -0
- package/dist/ask/index-file.d.ts.map +1 -1
- package/dist/ask/index-file.js +1 -1
- package/dist/ask/index-file.js.map +1 -1
- package/dist/cli-meta.d.ts +6 -1
- package/dist/cli-meta.d.ts.map +1 -1
- package/dist/cli-meta.js +8 -2
- package/dist/cli-meta.js.map +1 -1
- package/dist/cli-picker.d.ts.map +1 -1
- package/dist/cli-picker.js +3 -1
- package/dist/cli-picker.js.map +1 -1
- package/dist/cli.js +30 -2
- package/dist/cli.js.map +1 -1
- package/dist/context/build.d.ts.map +1 -1
- package/dist/context/build.js +13 -4
- package/dist/context/build.js.map +1 -1
- package/dist/context/check.d.ts.map +1 -1
- package/dist/context/check.js +14 -5
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +4 -18
- package/dist/context/node-file.js.map +1 -1
- package/dist/graph/build.d.ts +3 -0
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +41 -12
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cards.d.ts.map +1 -1
- package/dist/graph/cards.js +3 -2
- package/dist/graph/cards.js.map +1 -1
- package/dist/graph/check.d.ts.map +1 -1
- package/dist/graph/check.js +7 -4
- package/dist/graph/check.js.map +1 -1
- package/dist/graph/extract-cache.d.ts +1 -0
- package/dist/graph/extract-cache.d.ts.map +1 -1
- package/dist/graph/extract-cache.js +1 -1
- package/dist/graph/extract-cache.js.map +1 -1
- package/dist/graph/extract.d.ts +17 -0
- package/dist/graph/extract.d.ts.map +1 -1
- package/dist/graph/extract.js +188 -17
- package/dist/graph/extract.js.map +1 -1
- package/dist/graph/fingerprint.d.ts +1 -0
- package/dist/graph/fingerprint.d.ts.map +1 -1
- package/dist/graph/fingerprint.js +15 -6
- package/dist/graph/fingerprint.js.map +1 -1
- package/dist/graph/map.d.ts.map +1 -1
- package/dist/graph/map.js +6 -4
- package/dist/graph/map.js.map +1 -1
- package/dist/graph/refresh.d.ts.map +1 -1
- package/dist/graph/refresh.js +61 -13
- package/dist/graph/refresh.js.map +1 -1
- package/dist/graph/resolve.d.ts.map +1 -1
- package/dist/graph/resolve.js +36 -43
- package/dist/graph/resolve.js.map +1 -1
- package/dist/graph/scopes.d.ts +14 -2
- package/dist/graph/scopes.d.ts.map +1 -1
- package/dist/graph/scopes.js +51 -81
- package/dist/graph/scopes.js.map +1 -1
- package/dist/graph/seed.d.ts +38 -0
- package/dist/graph/seed.d.ts.map +1 -0
- package/dist/graph/seed.js +226 -0
- package/dist/graph/seed.js.map +1 -0
- package/dist/graph/source-files.d.ts +13 -5
- package/dist/graph/source-files.d.ts.map +1 -1
- package/dist/graph/source-files.js +16 -7
- package/dist/graph/source-files.js.map +1 -1
- package/dist/graph/traverse-cli.d.ts +8 -2
- package/dist/graph/traverse-cli.d.ts.map +1 -1
- package/dist/graph/traverse-cli.js +26 -6
- package/dist/graph/traverse-cli.js.map +1 -1
- package/dist/graph/traverse.d.ts +3 -1
- package/dist/graph/traverse.d.ts.map +1 -1
- package/dist/graph/traverse.js +22 -3
- package/dist/graph/traverse.js.map +1 -1
- package/dist/graph/types.d.ts +1 -0
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/workspace-cli.d.ts +5 -0
- package/dist/graph/workspace-cli.d.ts.map +1 -1
- package/dist/graph/workspace-cli.js +7 -0
- package/dist/graph/workspace-cli.js.map +1 -1
- package/dist/graph/workspace.js +1 -1
- package/dist/graph/workspace.js.map +1 -1
- package/dist/ingest/fs.d.ts +30 -2
- package/dist/ingest/fs.d.ts.map +1 -1
- package/dist/ingest/fs.js +84 -8
- package/dist/ingest/fs.js.map +1 -1
- package/dist/mcp/tools.js +3 -3
- package/dist/mcp/tools.js.map +1 -1
- package/dist/search/grep-cli.d.ts.map +1 -1
- package/dist/search/grep-cli.js +4 -1
- package/dist/search/grep-cli.js.map +1 -1
- package/dist/search/grep.d.ts +2 -1
- package/dist/search/grep.d.ts.map +1 -1
- package/dist/search/grep.js +16 -3
- package/dist/search/grep.js.map +1 -1
- package/dist/util/paths.d.ts +29 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +71 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/source.d.ts +4 -0
- package/dist/util/source.d.ts.map +1 -0
- package/dist/util/source.js +12 -0
- package/dist/util/source.js.map +1 -0
- package/dist/util/state.d.ts +25 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js +41 -0
- package/dist/util/state.js.map +1 -1
- package/package.json +2 -2
- package/scripts/run-tests.mjs +43 -0
package/dist/graph/refresh.js
CHANGED
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
* at the end of a turn — so retrieval stays cheap and a read stays a read. It
|
|
26
26
|
* also leaves `stats.json` alone, so that same `Stop` hook still sees `dirty` and
|
|
27
27
|
* still rebuilds the passive surface.
|
|
28
|
+
*
|
|
29
|
+
* One case is not drift but absence: inside a git worktree there is no graph at all,
|
|
30
|
+
* because `graft/` is gitignored and so was never checked out. The gate covers that
|
|
31
|
+
* too — it copies the parent checkout's graph in (`./seed.ts`) and then treats the
|
|
32
|
+
* difference between the two checkouts as ordinary drift, which is exactly what it is.
|
|
28
33
|
*/
|
|
29
34
|
import { existsSync } from "node:fs";
|
|
30
35
|
import { join, resolve } from "node:path";
|
|
@@ -34,6 +39,7 @@ import { CACHE_DIR } from "../context/node-file.js";
|
|
|
34
39
|
import { buildGraph } from "./build.js";
|
|
35
40
|
import { driftCount, isClean, probeDrift } from "./fingerprint.js";
|
|
36
41
|
import { invalidateGraphCaches } from "./load.js";
|
|
42
|
+
import { seedGraph } from "./seed.js";
|
|
37
43
|
import { wiringPath } from "./write.js";
|
|
38
44
|
/** How long to wait for another process's in-flight rebuild before giving up and
|
|
39
45
|
* answering from the current graph. Long enough to ride out a small repo's build,
|
|
@@ -90,6 +96,30 @@ async function waitForLock(cache) {
|
|
|
90
96
|
await sleep(LOCK_POLL_MS);
|
|
91
97
|
}
|
|
92
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Copy a parent checkout's graph in, holding the same lock a rebuild would.
|
|
101
|
+
*
|
|
102
|
+
* Two MCP tool calls in a fresh worktree arrive here at the same moment, and an 8 MB
|
|
103
|
+
* copy is long enough for both to start one. The loser waits, then finds the graph
|
|
104
|
+
* already there and reports nothing — which is why the caller re-checks the file
|
|
105
|
+
* rather than trusting this return value.
|
|
106
|
+
*/
|
|
107
|
+
async function seedUnderLock(dir, outDir, contextDir) {
|
|
108
|
+
const lockCache = join(outDir, CACHE_DIR);
|
|
109
|
+
// `busy` matters: with no graph on disk there is nothing to fall back to, so a
|
|
110
|
+
// caller that stayed silent here would emit the bare "no matching nodes" this whole
|
|
111
|
+
// mechanism exists to remove. Say what happened instead.
|
|
112
|
+
if (!(await waitForLock(lockCache)))
|
|
113
|
+
return { seeded: false, busy: true };
|
|
114
|
+
const unhook = releaseOnSignal(lockCache);
|
|
115
|
+
try {
|
|
116
|
+
return seedGraph(dir, { contextDir });
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
unhook();
|
|
120
|
+
releaseLockIn(lockCache);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
93
123
|
/**
|
|
94
124
|
* Rebuild `root`'s structural graph if the working tree has moved since the last
|
|
95
125
|
* build. Cheap and side-effect-free when nothing changed, which is the usual case.
|
|
@@ -103,26 +133,42 @@ export async function ensureFreshGraph(root, opts = {}) {
|
|
|
103
133
|
try {
|
|
104
134
|
const dir = resolve(root);
|
|
105
135
|
const outDir = contextDirFor(dir, opts.contextDir);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
let seededFrom;
|
|
137
|
+
if (!existsSync(wiringPath(outDir))) {
|
|
138
|
+
// One case has a graph to work with even though this checkout has none: a git
|
|
139
|
+
// worktree, whose parent checkout's `graft/` git could not check out. Copy it
|
|
140
|
+
// in and carry on — the drift below is then exactly the diff between the two
|
|
141
|
+
// checkouts, and repairing it is what makes the copied graph honest here.
|
|
142
|
+
const seed = await seedUnderLock(dir, outDir, opts.contextDir);
|
|
143
|
+
seededFrom = seed.from;
|
|
144
|
+
// Still nothing (not a worktree, parent never built, or a concurrent seed we
|
|
145
|
+
// lost the race for and which we now re-check for): the caller's own "no graph
|
|
146
|
+
// — run graft build" message is the right answer. Auto-building a whole repo
|
|
147
|
+
// under a query is a surprise, and it's the one case where the user hasn't
|
|
148
|
+
// opted into graft at all yet.
|
|
149
|
+
if (!existsSync(wiringPath(outDir))) {
|
|
150
|
+
return seed.busy
|
|
151
|
+
? { refreshed: false, note: "another process is still copying the graph into this worktree — retry the query in a moment" }
|
|
152
|
+
: CLEAN;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const seedNote = seededFrom ? `copied the graph from the main checkout (${seededFrom})` : undefined;
|
|
111
156
|
// null = no fingerprint at all: a graph built before this mechanism existed,
|
|
112
157
|
// or by a different extractor build. Rebuild once — that lays the fingerprint
|
|
113
158
|
// down, so it costs exactly one build, not one per query.
|
|
114
159
|
const drift = probeDrift(dir, outDir);
|
|
115
160
|
if (drift && isClean(drift))
|
|
116
|
-
return CLEAN;
|
|
161
|
+
return seedNote ? { refreshed: false, note: seedNote } : CLEAN;
|
|
117
162
|
// On the default layout this is `<root>/graft/.cache/.sync.lock`, the very file
|
|
118
163
|
// the Claude Code hooks lock — so this refresh and the background sync can
|
|
119
164
|
// never rebuild at the same time.
|
|
120
165
|
const lockCache = join(outDir, CACHE_DIR);
|
|
121
166
|
if (!(await waitForLock(lockCache))) {
|
|
167
|
+
const busy = "a graph rebuild is already in flight — answering from the current graph";
|
|
122
168
|
return {
|
|
123
169
|
refreshed: false,
|
|
124
170
|
drift: drift ?? undefined,
|
|
125
|
-
note:
|
|
171
|
+
note: seedNote ? `${seedNote}; ${busy}` : busy,
|
|
126
172
|
};
|
|
127
173
|
}
|
|
128
174
|
const unhook = releaseOnSignal(lockCache);
|
|
@@ -136,7 +182,7 @@ export async function ensureFreshGraph(root, opts = {}) {
|
|
|
136
182
|
const now = drift ? probeDrift(dir, outDir) : null;
|
|
137
183
|
if (now && isClean(now)) {
|
|
138
184
|
invalidateGraphCaches(outDir);
|
|
139
|
-
return CLEAN;
|
|
185
|
+
return seedNote ? { refreshed: false, note: seedNote } : CLEAN;
|
|
140
186
|
}
|
|
141
187
|
// Tier-1 only: no summarizer, so no LLM call and no network, ever. And
|
|
142
188
|
// `graphOnly`: write the graph, the ask sidecar and the fingerprint, nothing
|
|
@@ -145,7 +191,7 @@ export async function ensureFreshGraph(root, opts = {}) {
|
|
|
145
191
|
// card's mtime, and skipping them is most of what keeps this cheap.
|
|
146
192
|
await buildGraph(dir, { contextDir: opts.contextDir, graphOnly: true });
|
|
147
193
|
invalidateGraphCaches(outDir);
|
|
148
|
-
return { refreshed: true, drift: drift ?? undefined };
|
|
194
|
+
return { refreshed: true, drift: drift ?? undefined, note: seedNote };
|
|
149
195
|
}
|
|
150
196
|
finally {
|
|
151
197
|
unhook();
|
|
@@ -192,11 +238,13 @@ export async function ensureFreshChildren(root, children, opts = {}) {
|
|
|
192
238
|
/** The one-line note a CLI/MCP surface prints when a refresh actually happened.
|
|
193
239
|
* Null when there's nothing to say (the overwhelmingly common case). */
|
|
194
240
|
export function refreshNote(r) {
|
|
195
|
-
if (r.note)
|
|
196
|
-
return `[graft] ${r.note}`;
|
|
197
241
|
if (!r.refreshed)
|
|
198
|
-
return null;
|
|
242
|
+
return r.note ? `[graft] ${r.note}` : null;
|
|
199
243
|
const n = r.drift ? driftCount(r.drift) : 0;
|
|
200
|
-
|
|
244
|
+
const built = `refreshed the graph (${n || "?"} file${n === 1 ? "" : "s"} changed) before answering`;
|
|
245
|
+
// Both facts, when a worktree was seeded *and* the copy needed repairing: the
|
|
246
|
+
// count is the interesting half (it's the branch diff), the provenance explains
|
|
247
|
+
// where a graph came from in a directory the user knows they never built.
|
|
248
|
+
return r.note ? `[graft] ${built} — ${r.note}` : `[graft] ${built}`;
|
|
201
249
|
}
|
|
202
250
|
//# sourceMappingURL=refresh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/graph/refresh.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/graph/refresh.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/graph/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/graph/resolve.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAQ,MAAM,EAAY,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C;;sDAEsD;AACtD,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B;;6FAEyF;IACzF,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;CACxB;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,GAAE,cAAmB,GACxB,MAAM,EAAE,CAuFV"}
|
package/dist/graph/resolve.js
CHANGED
|
@@ -5,29 +5,16 @@
|
|
|
5
5
|
* Confidence mirrors the SCIP/Graphify model:
|
|
6
6
|
* - `extracted`: the target is certain — a match within the same file, an
|
|
7
7
|
* import specifier, or a structural containment.
|
|
8
|
-
* - `inferred`:
|
|
9
|
-
* which name-shadowing could in principle fool.
|
|
8
|
+
* - `inferred`: a bare function target was resolved by a unique name match
|
|
9
|
+
* across files, which name-shadowing could in principle fool.
|
|
10
10
|
* Ambiguous cross-file matches (a name defined in several files) are dropped
|
|
11
|
-
* rather than guessed
|
|
11
|
+
* rather than guessed. Member calls are stricter: they require a receiver type
|
|
12
|
+
* and owner-qualified method match because a unique bare method name says
|
|
13
|
+
* nothing about the receiver.
|
|
12
14
|
*/
|
|
13
15
|
import { posix } from "node:path";
|
|
14
|
-
import {
|
|
16
|
+
import { toPosixPath } from "../util/paths.js";
|
|
15
17
|
const IMPORT_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs", ".py"];
|
|
16
|
-
// Builtin container/value types whose methods are never user-defined symbols in the
|
|
17
|
-
// repo. A typed member call on one of these receivers (`deg.set(...)` where
|
|
18
|
-
// `recvType` is `Map`) must never fall back to the untyped unique-bare-name path —
|
|
19
|
-
// doing so wires it to whatever repo symbol happens to share the method's name
|
|
20
|
-
// (e.g. a class's own `.set` method), which is always wrong. RawEdge carries no
|
|
21
|
-
// language tag, so this is the union of the TS/TSX and Python builtin lists rather
|
|
22
|
-
// than a per-language check; a repo class legitimately named e.g. `dict` colliding
|
|
23
|
-
// with the Python builtin is vanishingly rare, and the failure mode in that case is
|
|
24
|
-
// just a dropped edge (safe), not a wrong one.
|
|
25
|
-
const BUILTIN_CONTAINER_TYPES = new Set([
|
|
26
|
-
// TS/TSX
|
|
27
|
-
"Map", "Set", "WeakMap", "WeakSet", "Array", "Promise", "Object", "Date", "RegExp", "Error",
|
|
28
|
-
// Python
|
|
29
|
-
"dict", "list", "set", "tuple", "str", "frozenset", "bytes",
|
|
30
|
-
]);
|
|
31
18
|
export function resolveEdges(nodes, rawEdges, opts = {}) {
|
|
32
19
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
33
20
|
const globalName = new Map();
|
|
@@ -41,7 +28,7 @@ export function resolveEdges(nodes, rawEdges, opts = {}) {
|
|
|
41
28
|
for (const n of nodes) {
|
|
42
29
|
if (n.kind === "file") {
|
|
43
30
|
if (hasGoModules && n.path.endsWith(".go")) {
|
|
44
|
-
const dir = posix.dirname(n.path
|
|
31
|
+
const dir = posix.dirname(toPosixPath(n.path));
|
|
45
32
|
push(goFilesByDir, dir, n.id);
|
|
46
33
|
}
|
|
47
34
|
continue;
|
|
@@ -51,11 +38,8 @@ export function resolveEdges(nodes, rawEdges, opts = {}) {
|
|
|
51
38
|
if (!fileMap)
|
|
52
39
|
perFileName.set(n.path, (fileMap = new Map()));
|
|
53
40
|
push(fileMap, n.name, n);
|
|
54
|
-
if (n.kind === "method") {
|
|
55
|
-
|
|
56
|
-
const segs = post.split(".");
|
|
57
|
-
if (segs.length >= 2)
|
|
58
|
-
push(ownerMethod, `${segs[segs.length - 2]}.${segs[segs.length - 1]}`, n);
|
|
41
|
+
if (n.kind === "method" && n.owner) {
|
|
42
|
+
push(ownerMethod, `${n.owner}.${n.name}`, n);
|
|
59
43
|
}
|
|
60
44
|
}
|
|
61
45
|
// classParents: class/interface name → its declared base-class names, from raw
|
|
@@ -65,8 +49,12 @@ export function resolveEdges(nodes, rawEdges, opts = {}) {
|
|
|
65
49
|
for (const e of rawEdges) {
|
|
66
50
|
if (e.relation !== "extends" || !e.name)
|
|
67
51
|
continue;
|
|
68
|
-
|
|
69
|
-
|
|
52
|
+
// The declaring class's own bare name — read from its node (keyed by n.name, set
|
|
53
|
+
// once at mint time) rather than re-derived by slicing e.source, which breaks once
|
|
54
|
+
// ids can carry a dedup ordinal (A3's `Cache~2`).
|
|
55
|
+
const ownName = byId.get(e.source)?.name;
|
|
56
|
+
if (!ownName)
|
|
57
|
+
continue;
|
|
70
58
|
push(classParents, ownName, e.name);
|
|
71
59
|
}
|
|
72
60
|
const out = [];
|
|
@@ -94,27 +82,31 @@ export function resolveEdges(nodes, rawEdges, opts = {}) {
|
|
|
94
82
|
// an unresolved base is usually an external/imported type — keep the name.
|
|
95
83
|
add(e.source, hit?.id ?? e.name, e.relation, hit?.confidence ?? "inferred");
|
|
96
84
|
}
|
|
85
|
+
else if (e.relation === "references" && e.name && e.specifier) {
|
|
86
|
+
// A named import gives both halves needed for sound resolution: the module
|
|
87
|
+
// it came from and the exported name. Resolve inside that file only, so a
|
|
88
|
+
// same-named symbol elsewhere in the repo cannot become a false edge.
|
|
89
|
+
const targetFile = resolveImport(e.specifier, e.file, byId);
|
|
90
|
+
if (!byId.has(targetFile))
|
|
91
|
+
continue; // external or unresolved module
|
|
92
|
+
const candidates = perFileName.get(targetFile)?.get(e.name) ?? [];
|
|
93
|
+
if (candidates.length === 1)
|
|
94
|
+
add(e.source, candidates[0].id, "references", "extracted");
|
|
95
|
+
}
|
|
97
96
|
else if (e.relation === "calls") {
|
|
98
|
-
if (e.viaMember
|
|
97
|
+
if (e.viaMember) {
|
|
98
|
+
if (!e.recvType)
|
|
99
|
+
continue;
|
|
99
100
|
const hit = resolveTypedMember(e.recvType, e.name, e.file, ownerMethod, classParents);
|
|
100
101
|
if (hit === "ambiguous")
|
|
101
102
|
continue; // drop — never guess past an ambiguous owner
|
|
102
103
|
if (hit)
|
|
103
104
|
add(e.source, hit.id, "calls", hit.confidence);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// existing untyped path (unique bare-name match), unchanged.
|
|
107
|
-
const fallback = resolveName(e.name, e.file, ["method"], perFileName, globalName);
|
|
108
|
-
if (fallback)
|
|
109
|
-
add(e.source, fallback.id, "calls", fallback.confidence);
|
|
110
|
-
}
|
|
111
|
-
// else: recvType is a known builtin container (Map/dict/etc) — its methods
|
|
112
|
-
// are never user-defined repo symbols, so drop rather than wire a bare-name
|
|
113
|
-
// fallback to an unrelated same-named method.
|
|
105
|
+
// No owner-qualified match means the call is unresolved. A unique bare
|
|
106
|
+
// method name is not evidence that this receiver has that method.
|
|
114
107
|
continue;
|
|
115
108
|
}
|
|
116
|
-
const
|
|
117
|
-
const hit = resolveName(e.name, e.file, kinds, perFileName, globalName);
|
|
109
|
+
const hit = resolveName(e.name, e.file, ["function"], perFileName, globalName);
|
|
118
110
|
if (hit)
|
|
119
111
|
add(e.source, hit.id, "calls", hit.confidence); // drop unresolved calls (too noisy)
|
|
120
112
|
}
|
|
@@ -152,8 +144,7 @@ function resolveName(name, file, kinds, perFileName, globalName) {
|
|
|
152
144
|
* per the inviolable philosophy we drop and stop rather than guess, and we do
|
|
153
145
|
* NOT continue up the chain past this level.
|
|
154
146
|
* - `null` — the whole chain (recvType + ancestors, breadth-first, depth ≤ 3,
|
|
155
|
-
* cycle-guarded) had zero candidates at every level
|
|
156
|
-
* the untyped bare-name path.
|
|
147
|
+
* cycle-guarded) had zero candidates at every level.
|
|
157
148
|
*/
|
|
158
149
|
function resolveTypedMember(recvType, name, file, ownerMethod, classParents) {
|
|
159
150
|
const MAX_DEPTH = 3;
|
|
@@ -193,7 +184,9 @@ function resolveTypedMember(recvType, name, file, ownerMethod, classParents) {
|
|
|
193
184
|
function resolveImport(spec, file, byId) {
|
|
194
185
|
if (!spec.startsWith("."))
|
|
195
186
|
return spec;
|
|
196
|
-
|
|
187
|
+
// Belt-and-braces: `node.path` is posix by construction (`../util/paths.ts`),
|
|
188
|
+
// but this also accepts a hand-written or hand-edited graph.
|
|
189
|
+
const dir = posix.dirname(toPosixPath(file));
|
|
197
190
|
const base = posix.normalize(posix.join(dir, spec));
|
|
198
191
|
const noExt = base.replace(/\.(js|jsx|mjs|cjs|ts|tsx|py)$/, "");
|
|
199
192
|
const candidates = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/graph/resolve.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/graph/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAiB1F,MAAM,UAAU,YAAY,CAC1B,KAAe,EACf,QAAmB,EACnB,OAAuB,EAAE;IAEzB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC7D,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAChD,oFAAoF;IACpF,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,YAAY,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/C,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAChC,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5B,IAAI,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO;YAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,6EAA6E;IAC7E,uEAAuE;IACvE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,SAAS;QAClD,iFAAiF;QACjF,mFAAmF;QACnF,kDAAkD;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;QACzC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,MAAc,EAAE,QAAkB,EAAE,UAAgC,EAAE,EAAE;QACnG,MAAM,GAAG,GAAG,GAAG,MAAM,KAAK,QAAQ,KAAK,MAAM,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QAC1B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5C,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACrD,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;YACnD,MAAM,MAAM,GACV,YAAY,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACpC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,SAAU,EAAE,YAAY,CAAC;gBAC7D,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC/C,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QAChD,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;YACnE,MAAM,KAAK,GAAW,CAAC,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YAC3F,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,IAAK,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;YACzE,2EAA2E;YAC3E,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,IAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,GAAG,EAAE,UAAU,IAAI,UAAU,CAAC,CAAC;QAC/E,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;YAChE,2EAA2E;YAC3E,0EAA0E;YAC1E,sEAAsE;YACtE,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC5D,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,SAAS,CAAC,gCAAgC;YACrE,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAClE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;QAC1F,CAAC;aAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;gBAChB,IAAI,CAAC,CAAC,CAAC,QAAQ;oBAAE,SAAS;gBAC1B,MAAM,GAAG,GAAG,kBAAkB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAK,EAAE,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;gBACvF,IAAI,GAAG,KAAK,WAAW;oBAAE,SAAS,CAAC,6CAA6C;gBAChF,IAAI,GAAG;oBAAE,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACxD,uEAAuE;gBACvE,kEAAkE;gBAClE,SAAS;YACX,CAAC;YACD,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,IAAK,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;YAChF,IAAI,GAAG;gBAAE,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,oCAAoC;QAC/F,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAI,GAAqB,EAAE,GAAW,EAAE,GAAM;IACzD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,GAAG;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;QAClB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAClB,IAAY,EACZ,IAAY,EACZ,KAAa,EACb,WAA+C,EAC/C,UAAiC;IAEjC,MAAM,KAAK,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7F,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;IACtE,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAClF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IAC7E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,kBAAkB,CACzB,QAAgB,EAChB,IAAY,EACZ,IAAY,EACZ,WAAkC,EAClC,YAAmC;IAEnC,MAAM,SAAS,GAAG,CAAC,CAAC;IACpB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5C,IAAI,QAAQ,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QACnE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS,CAAC,oBAAoB;YAC1E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;gBACxB,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;YAC9E,CAAC;YACD,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YACzD,IAAI,QAAQ;gBAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YAClE,OAAO,WAAW,CAAC,CAAC,0CAA0C;QAChE,CAAC;QACD,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBAClD,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,SAAS;gBAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACpB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC,CAAC,yCAAyC;AACxD,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,IAAY,EAAE,IAAY,EAAE,IAAyB;IAC1E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,8EAA8E;IAC9E,6DAA6D;IAC7D,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG;QACjB,IAAI;QACJ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;QACpC,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,CAAC;KAChD,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU;QAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,OAAmB,EAAE,UAAiC;IAC3F,IAAI,IAAI,GAA8C,IAAI,CAAC;IAC3D,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,OAAO,GAAkB,IAAI,CAAC;QAClC,IAAI,IAAI,KAAK,GAAG,CAAC,MAAM;YAAE,OAAO,GAAG,EAAE,CAAC;aACjC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACxF,IAAI,OAAO,KAAK,IAAI;YAAE,SAAS;QAC/B,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM;YAAE,IAAI,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;IACnF,CAAC;IACD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,+CAA+C;IAEvE,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/graph/scopes.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { GraphV1, ScopeV1 } from "./types.js";
|
|
2
|
-
/** Walk the tree (reusing `walkDir`'s skip rules
|
|
3
|
-
|
|
2
|
+
/** Walk the tree (reusing `walkDir`'s skip rules, including the repo's persisted
|
|
3
|
+
* `--include-dir` override) and find project-marker dirs. */
|
|
4
|
+
export declare function discoverScopes(root: string, repoFiles?: string[]): ScopeV1[];
|
|
4
5
|
/** Nearest-prefix owner. `scopes` MUST be sorted prefix-length desc; "" matches all.
|
|
5
6
|
* Falls back to a synthetic root scope when `scopes` has no explicit "" entry and
|
|
6
7
|
* nothing else matches — every path resolves to some scope. */
|
|
@@ -20,6 +21,17 @@ export declare function scopesHereClause(scopes: ScopeV1[]): string;
|
|
|
20
21
|
* (`""`) matches every path. Used to filter ask's doc/node set for
|
|
21
22
|
* `--in <path-prefix>`. */
|
|
22
23
|
export declare function pathUnderPrefix(path: string, prefix: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Validate a normalized `--in` prefix against the graph before a query runs. A
|
|
26
|
+
* prefix matching no indexed node is a caller mistake (typo, wrong sub-project,
|
|
27
|
+
* or a mid-path fragment where a prefix is required) rather than a legitimate
|
|
28
|
+
* zero-hit query, so every `--in`-taking command fails loudly and identically
|
|
29
|
+
* instead of printing empty output the caller has to interpret.
|
|
30
|
+
*
|
|
31
|
+
* Pass the prefix through `normalizePathPrefix` first — an un-normalized
|
|
32
|
+
* `src\gpu` or `src/` will not match anything here.
|
|
33
|
+
*/
|
|
34
|
+
export declare function assertPrefixIndexed(graph: GraphV1, prefix: string): void;
|
|
23
35
|
/** Immediate subdirs of `root` that are themselves git repos (have `.git`).
|
|
24
36
|
* Used by workspace federation (Task 5). */
|
|
25
37
|
export declare function discoverWorkspaceChildren(root: string): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/graph/scopes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/graph/scopes.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAsGnD;6DAC6D;AAC7D,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAM,EAAkD,GAClE,OAAO,EAAE,CAqIX;AAED;;+DAE+D;AAC/D,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAMhE;AAED;;+DAE+D;AAC/D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;sCAGsC;AACtC,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAG1D;AAED;;;;2BAI2B;AAC3B,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKxE;AAED;4CAC4C;AAC5C,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAYhE;AAED;;2EAE2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,CAEvD"}
|
package/dist/graph/scopes.js
CHANGED
|
@@ -21,37 +21,26 @@
|
|
|
21
21
|
* 7. `label` = `prefix` (both "" for root); ordering is prefix-length desc,
|
|
22
22
|
* then lexicographic.
|
|
23
23
|
*/
|
|
24
|
-
import { existsSync, readdirSync, readFileSync
|
|
24
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
25
25
|
import { join, resolve } from "node:path";
|
|
26
|
-
import {
|
|
26
|
+
import { shouldSkipDir, walkDir } from "../ingest/fs.js";
|
|
27
|
+
import { relPosix } from "../util/paths.js";
|
|
28
|
+
import { readIncludeDirs } from "../util/state.js";
|
|
27
29
|
/** Project-marker files, checked in this order (also the order `markers` is built in). */
|
|
28
30
|
const MARKERS = ["package.json", "go.mod", "pyproject.toml", "setup.py", "Cargo.toml"];
|
|
29
31
|
const CANONICAL_ROOT = [{ prefix: "", label: "", markers: [] }];
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
function collectDirs(root) {
|
|
34
|
-
const out = [""];
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
if (!entry.isDirectory())
|
|
45
|
-
continue;
|
|
46
|
-
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name))
|
|
47
|
-
continue;
|
|
48
|
-
const rel = relDir === "" ? entry.name : `${relDir}/${entry.name}`;
|
|
49
|
-
out.push(rel);
|
|
50
|
-
walk(join(absDir, entry.name), rel);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
walk(root, "");
|
|
54
|
-
return out;
|
|
32
|
+
/** Collect directories represented by the canonical visible file set. Building
|
|
33
|
+
* this from `walkDir` keeps scope discovery aligned with extraction: an ignored
|
|
34
|
+
* generated tree cannot become a marker or workspace scope. */
|
|
35
|
+
function collectDirs(root, repoFiles) {
|
|
36
|
+
const out = new Set([""]);
|
|
37
|
+
for (const abs of repoFiles) {
|
|
38
|
+
const parts = relPosix(root, abs).split("/");
|
|
39
|
+
parts.pop();
|
|
40
|
+
for (let i = 1; i <= parts.length; i++)
|
|
41
|
+
out.add(parts.slice(0, i).join("/"));
|
|
42
|
+
}
|
|
43
|
+
return [...out];
|
|
55
44
|
}
|
|
56
45
|
/** Minimal `packages:` list parser for `pnpm-workspace.yaml` — no YAML dependency,
|
|
57
46
|
* handles the `packages:\n - 'glob'\n - "glob"` form pnpm actually generates. */
|
|
@@ -104,10 +93,9 @@ function readWorkspaceGlobs(root) {
|
|
|
104
93
|
}
|
|
105
94
|
return null;
|
|
106
95
|
}
|
|
107
|
-
/** Resolve one workspace glob
|
|
108
|
-
*
|
|
109
|
-
|
|
110
|
-
function resolveGlob(root, pattern) {
|
|
96
|
+
/** Resolve one workspace glob against visible directories only. Supports
|
|
97
|
+
* `dir/*` (immediate subdirs), `dir/**` (any nested depth), and literal dirs. */
|
|
98
|
+
function resolveGlob(dirs, pattern) {
|
|
111
99
|
const norm = pattern.replace(/\\/g, "/").replace(/\/$/, "");
|
|
112
100
|
let base;
|
|
113
101
|
let recursive;
|
|
@@ -128,57 +116,26 @@ function resolveGlob(root, pattern) {
|
|
|
128
116
|
recursive = false;
|
|
129
117
|
}
|
|
130
118
|
else if (!norm.includes("*")) {
|
|
131
|
-
|
|
132
|
-
// workspace-list entry that names a real directory counts as a workspace match
|
|
133
|
-
// in its own right, same as a resolved glob would. Without this, a literal
|
|
134
|
-
// entry silently resolves to nothing while Rule 2 still strips its
|
|
135
|
-
// `package.json` marker (it's a JS-family dir that never matched the glob
|
|
136
|
-
// set), de-scoping a real workspace package.
|
|
137
|
-
try {
|
|
138
|
-
return existsSync(join(root, norm)) && statSync(join(root, norm)).isDirectory()
|
|
139
|
-
? [norm]
|
|
140
|
-
: [];
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
return [];
|
|
144
|
-
}
|
|
119
|
+
return dirs.includes(norm) ? [norm] : [];
|
|
145
120
|
}
|
|
146
121
|
else {
|
|
147
122
|
return []; // unsupported glob form
|
|
148
123
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return entries
|
|
159
|
-
.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SKIP_DIRS.has(e.name))
|
|
160
|
-
.map((e) => (relDir === "" ? e.name : `${relDir}/${e.name}`));
|
|
161
|
-
};
|
|
162
|
-
const baseAbs = base === "" ? root : join(root, base);
|
|
163
|
-
if (!recursive) {
|
|
164
|
-
results.push(...listDirs(baseAbs, base));
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
const stack = [base];
|
|
168
|
-
while (stack.length) {
|
|
169
|
-
const relDir = stack.pop();
|
|
170
|
-
const absDir = relDir === "" ? root : join(root, relDir);
|
|
171
|
-
const children = listDirs(absDir, relDir);
|
|
172
|
-
results.push(...children);
|
|
173
|
-
stack.push(...children);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return results;
|
|
124
|
+
return dirs.filter((dir) => {
|
|
125
|
+
if (dir === "")
|
|
126
|
+
return false;
|
|
127
|
+
if (recursive)
|
|
128
|
+
return base === "" || dir.startsWith(`${base}/`);
|
|
129
|
+
const slash = dir.lastIndexOf("/");
|
|
130
|
+
const parent = slash === -1 ? "" : dir.slice(0, slash);
|
|
131
|
+
return parent === base;
|
|
132
|
+
});
|
|
177
133
|
}
|
|
178
|
-
/** Walk the tree (reusing `walkDir`'s skip rules
|
|
179
|
-
|
|
134
|
+
/** Walk the tree (reusing `walkDir`'s skip rules, including the repo's persisted
|
|
135
|
+
* `--include-dir` override) and find project-marker dirs. */
|
|
136
|
+
export function discoverScopes(root, repoFiles = walkDir(root, readIncludeDirs(resolve(root)))) {
|
|
180
137
|
const absRoot = resolve(root);
|
|
181
|
-
const dirs = collectDirs(absRoot);
|
|
138
|
+
const dirs = collectDirs(absRoot, repoFiles);
|
|
182
139
|
const markerMap = new Map();
|
|
183
140
|
for (const dir of dirs) {
|
|
184
141
|
const found = MARKERS.filter((m) => existsSync(join(absRoot, dir, m)));
|
|
@@ -189,7 +146,7 @@ export function discoverScopes(root) {
|
|
|
189
146
|
const workspaceMatches = new Set();
|
|
190
147
|
if (workspaceGlobs) {
|
|
191
148
|
for (const glob of workspaceGlobs) {
|
|
192
|
-
for (const dir of resolveGlob(
|
|
149
|
+
for (const dir of resolveGlob(dirs, glob))
|
|
193
150
|
workspaceMatches.add(dir);
|
|
194
151
|
}
|
|
195
152
|
}
|
|
@@ -342,10 +299,26 @@ export function scopesHereClause(scopes) {
|
|
|
342
299
|
export function pathUnderPrefix(path, prefix) {
|
|
343
300
|
return prefix === "" || path === prefix || path.startsWith(`${prefix}/`);
|
|
344
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Validate a normalized `--in` prefix against the graph before a query runs. A
|
|
304
|
+
* prefix matching no indexed node is a caller mistake (typo, wrong sub-project,
|
|
305
|
+
* or a mid-path fragment where a prefix is required) rather than a legitimate
|
|
306
|
+
* zero-hit query, so every `--in`-taking command fails loudly and identically
|
|
307
|
+
* instead of printing empty output the caller has to interpret.
|
|
308
|
+
*
|
|
309
|
+
* Pass the prefix through `normalizePathPrefix` first — an un-normalized
|
|
310
|
+
* `src\gpu` or `src/` will not match anything here.
|
|
311
|
+
*/
|
|
312
|
+
export function assertPrefixIndexed(graph, prefix) {
|
|
313
|
+
if (!prefix || graph.nodes.some((n) => pathUnderPrefix(n.path, prefix)))
|
|
314
|
+
return;
|
|
315
|
+
throw new Error(`nothing indexed under "${prefix}/"${scopesHereClause(scopesOfGraph(graph))} (or any path prefix)`);
|
|
316
|
+
}
|
|
345
317
|
/** Immediate subdirs of `root` that are themselves git repos (have `.git`).
|
|
346
318
|
* Used by workspace federation (Task 5). */
|
|
347
319
|
export function discoverWorkspaceChildren(root) {
|
|
348
320
|
const absRoot = resolve(root);
|
|
321
|
+
const includes = readIncludeDirs(absRoot);
|
|
349
322
|
let entries;
|
|
350
323
|
try {
|
|
351
324
|
entries = readdirSync(absRoot, { withFileTypes: true });
|
|
@@ -354,10 +327,7 @@ export function discoverWorkspaceChildren(root) {
|
|
|
354
327
|
return [];
|
|
355
328
|
}
|
|
356
329
|
return entries
|
|
357
|
-
.filter((e) => e.isDirectory() &&
|
|
358
|
-
!e.name.startsWith(".") &&
|
|
359
|
-
!SKIP_DIRS.has(e.name) &&
|
|
360
|
-
existsSync(join(absRoot, e.name, ".git")))
|
|
330
|
+
.filter((e) => e.isDirectory() && !shouldSkipDir(e.name, includes) && existsSync(join(absRoot, e.name, ".git")))
|
|
361
331
|
.map((e) => e.name);
|
|
362
332
|
}
|
|
363
333
|
/** Every consumer's entry point to a graph's scopes: absent `meta.scopes` (old
|