Skip to content

code_index: misses symbols exported as const Name = memo(...) / forwardRef(...) (React idioms) #2

@nordscope-fi

Description

@nordscope-fi

Summary

code_index extracts symbols from direct declarations (function Foo(), class Foo) but silently skips const exports wrapped in HOFs like React's memo(), forwardRef(), and lazy(). This is a very common React idiom — in a typical TypeScript+React codebase, the majority of UI components fall into this pattern and become invisible to code_search, code_context, and code_impact.

This is a particularly bad failure mode because the empty result is silent — calling code_impact("MyComponent") returns "No symbols found", which an AI agent may interpret as "no callers, safe to delete" rather than "indexer didn't see this symbol."

Environment

  • CogniLayer v4.3.0
  • Project: TypeScript + React + Vite + Tailwind
  • Tree-sitter language pack: installed (other symbols index correctly)

Reproduction

In a .tsx file:

import { memo, forwardRef } from "react";

// Indexed correctly
export function useAuth() { /* ... */ }

// Not indexed
export const DiagnosticsTab = memo(function DiagnosticsTab({ analysis }) {
  return <div>...</div>;
});

// Same issue
export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
  return <button ref={ref} {...props} />;
});

After code_index({ full: true }):

code_search("useAuth")        -> Found, with full reference graph
code_search("DiagnosticsTab") -> "No symbols found"
code_search("Button")         -> "No symbols found"
code_search("memo")           -> "No symbols found" (despite being imported in 100+ files)

The tree-sitter AST appears to parse correctly. The gap is in symbol extraction — it doesn't recognize export const X = <Identifier>(<arg-with-name>) as defining a symbol X.

Suggested fix

When the extractor sees export const Foo = call(arg):

  1. Always register Foo as an exported symbol of kind const/variable.
  2. If the call argument is a named function expression (memo(function Bar() {})), register both Foo and Bar.
  3. If the wrapper is a known HOF (memo, forwardRef, lazy, observer, withRouter...), tag kind as react_component.

Even step (1) alone would close the gap for the common case.

Why this matters for AI agent workflows

CogniLayer is positioned as the code-intelligence layer for AI agents, with code_impact as a mandatory check before risky changes. With this gap, agents:

  • Run code_impact("ComponentName") -> empty
  • Conclude "no impact, safe to refactor"
  • Break dozens of callers

A README note about the limitation ("use grep as fallback for React components") would also help in the meantime.

Happy to PR a fix if you can point at the symbol-extraction module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions