CodeGraph already names an anonymous function after the variable_declarator it
is bound through — react_hook_bound_name does exactly this. The mechanism reads
as a general one, but it only runs when the callee is useCallback,
useEffectEvent or useEvent, so the same shape with any other wrapper yields
no function node at all.
Repro
declare const Effect: { fn: (n: string) => (b: unknown) => unknown };
declare function useCallback<T>(f: T, d: unknown[]): T;
function helper() { return 1; }
const viaHook = useCallback(() => { return helper(); }, []);
const viaEffect = Effect.fn("Session.run")(function* () { return helper(); });
After codegraph index:
nodes: helper · viaHook (function) · viaHook (constant) · viaEffect (constant)
edges: viaHook -> helper
b.ts -> helper <- the file, not a function
viaEffect gets a constant node but no function node. Its body is not simply
missing, either: the helper() call inside it is attributed to the file, so
the file picks up an outgoing call edge that belongs to a function, and
helper's caller list names the file rather than the function that actually
calls it.
What the two lines have in common
Both are an anonymous function passed as an argument to the call a
variable_declarator binds, and the declarator name is available in both. The
only difference is the callee's identity, checked against a literal list:
// codegraph-kernel/src/tsjs/mod.rs:769 — react_hook_bound_name
if !matches!(hook, "useCallback" | "useEffectEvent" | "useEvent") {
// src/extraction/tree-sitter.ts:396
const REACT_HANDLER_HOOKS = /^(?:React\.)?use(?:Callback|EffectEvent|Event)$/;
Other wrappers that land in the same position:
| Shape |
Ecosystem |
const run = Effect.fn("Session.run")(function* () {}) |
Effect-TS |
const C = connect(mapState)(function () {}) |
Redux |
const x = wrap("name")(function () {}) |
project-local helpers |
Scope
The shape is not rare. sst/opencode carries 1,056 distinct string-named
Effect.fn* wrappers across its packages. Indexing it, 48 of them end up with a
function node — the other 1,008 are functions missing from the graph, each one
also sending its body's calls to a file instead of to a function.
CodeGraph already names an anonymous function after the
variable_declaratoritis bound through —
react_hook_bound_namedoes exactly this. The mechanism readsas a general one, but it only runs when the callee is
useCallback,useEffectEventoruseEvent, so the same shape with any other wrapper yieldsno function node at all.
Repro
After
codegraph index:viaEffectgets a constant node but no function node. Its body is not simplymissing, either: the
helper()call inside it is attributed to the file, sothe file picks up an outgoing call edge that belongs to a function, and
helper's caller list names the file rather than the function that actuallycalls it.
What the two lines have in common
Both are an anonymous function passed as an argument to the call a
variable_declaratorbinds, and the declarator name is available in both. Theonly difference is the callee's identity, checked against a literal list:
Other wrappers that land in the same position:
const run = Effect.fn("Session.run")(function* () {})const C = connect(mapState)(function () {})const x = wrap("name")(function () {})Scope
The shape is not rare.
sst/opencodecarries 1,056 distinct string-namedEffect.fn*wrappers across its packages. Indexing it, 48 of them end up with afunction node — the other 1,008 are functions missing from the graph, each one
also sending its body's calls to a file instead of to a function.