MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / buildNode

Function buildNode

src/ui-server/api/node.ts:77–227  ·  view source on GitHub ↗
(cg: CodeGraph, projectRoot: string, nodeId: string)

Source from the content-addressed store, hash-verified

75}
76
77export async function buildNode(cg: CodeGraph, projectRoot: string, nodeId: string): Promise<unknown> {
78 const node = cg.getNode(nodeId);
79 if (!node) {
80 throw notFound(
81 'No symbol with that id is in this index.',
82 'Symbol ids change whenever the file is re-indexed — search for the symbol by ' +
83 'name instead of reusing an id from an older session.'
84 );
85 }
86
87 const incomingAll = cg.getIncomingEdges(nodeId);
88 const outgoingAll = cg.getOutgoingEdges(nodeId);
89
90 // `contains` is structure, not dependency: upward it is the parent (already in
91 // `ancestors`), downward it is the members outline. Leaving it in the rails
92 // would put a symbol's own class in its caller list.
93 const incoming = incomingAll.filter((e) => e.kind !== 'contains');
94 const outgoingRest: Edge[] = [];
95 const containsOut: Edge[] = [];
96 for (const edge of outgoingAll) {
97 if (edge.kind === 'contains') containsOut.push(edge);
98 else outgoingRest.push(edge);
99 }
100
101 const ancestors = cg.getAncestors(nodeId);
102
103 // ---------------------------------------------------------------------------
104 // One batched resolve for every endpoint this payload names.
105 // ---------------------------------------------------------------------------
106 const endpointIds = new Set<string>();
107 for (const edge of incoming) endpointIds.add(edge.source);
108 for (const edge of outgoingRest) endpointIds.add(edge.target);
109 for (const edge of containsOut) endpointIds.add(edge.target);
110 const endpoints = cg.getNodesByIds([...endpointIds]);
111
112 // A `references` edge into a type is "uses type X", not "calls X" — the
113 // header shows those as chips rather than as callee rows. Split at the EDGE
114 // level so a class that is both instantiated and named as a type appears in
115 // both places, which is what the source actually says.
116 const calleeEdges: Edge[] = [];
117 const typeRefs: Edge[] = [];
118 for (const edge of outgoingRest) {
119 const target = endpoints.get(edge.target);
120 if (edge.kind === 'references' && target && TYPE_KINDS.has(target.kind)) typeRefs.push(edge);
121 else calleeEdges.push(edge);
122 }
123
124 // ---------------------------------------------------------------------------
125 // Rails
126 // ---------------------------------------------------------------------------
127 const focalFile = toPosixPath(node.filePath);
128
129 const incomingGroups = groupRelations(incoming, (e) => e.source, endpoints);
130 incomingGroups.sort((a, b) => {
131 // The symbol's own file first ("same file" in the left rail), then
132 // production code, then tests — so a cap trims the least useful end.
133 const aSame = a.node.file === focalFile ? 0 : 1;
134 const bSame = b.node.file === focalFile ? 0 : 1;

Callers 4

dispatchPathRoutesFunction · 0.90

Calls 15

notFoundFunction · 0.90
groupRelationsFunction · 0.90
firstLineFunction · 0.90
annotateWhenFunction · 0.90
buildHierarchyFunction · 0.90
toNodeDetailFunction · 0.90
wireListFunction · 0.90
buildMembersFunction · 0.85
driftForFunction · 0.85
summarizeTestCallersFunction · 0.85
summarizeOutsideIndexFunction · 0.85
summarizeBlastFunction · 0.85

Tested by

no test coverage detected