Files
codegraph/__tests__/fixtures/kernel-parity/torture.tsx
T
Colby McHenryandClaude Fable 5 9ad5cd7ba2 feat(kernel): R2 — full TypeScript/JavaScript extraction port, byte-parity with the wasm path
Replaces the R1 seed .scm emitter with a bespoke Rust walker
(codegraph-kernel/src/tsjs/) that mirrors TreeSitterExtractor's TS/JS
paths function-for-function: declarations (incl. #808 field/property
classification), qualified names, docstrings (#780 wrapper climbs),
signatures, imports/re-exports + per-binding refs, calls with
receiver-qualified callees (#1230 literal-receiver skip), instantiations,
decorators, inheritance, type annotations (#381), type-alias members +
tuple contracts (#359/#634), React component recognition (#841
forwardRef/memo/styled), object-of-functions / zustand-through-middleware
/ RTK Query endpoints + generated hooks / vuex + pinia store shapes,
function-as-value capture with the flush gate (#756), and value-reference
edges with the shadow prune (#895/#897). The generic query emitter is
deleted — extraction parity needs logic .scm can't express; future
languages get walkers too (migration plan §4a).

Positions and JS string-slice semantics are emitted in UTF-16 code units
natively, so kernel output is byte-identical to web-tree-sitter's — no
column diff class exists.

Parity evidence (macOS): scripts/kernel-parity.mjs (full-object multiset
diff per file) — this repo 353/353 files, excalidraw 643/643 (10,650
nodes / 10,726 edges / 68,307 refs), plus torture fixtures checked into
__tests__/fixtures/kernel-parity/ and enforced in npm test by
kernel-tsjs-parity.test.ts. The strict compare caught one real decoder
bug the loose harness missed: refs must NOT carry denormalized
filePath/language at the extractFromSource seam (the store fills them).

Perf: extraction 2.6× single-thread on excalidraw (487ms vs 1,255ms,
identical outputs). Routing stays opt-in (CODEGRAPH_KERNEL_LANGS) until
the R3 equivalence gate (large repo, DB dump-diff, retrieval invariants,
agent A/B, Linux/Windows) passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:14:40 -05:00

215 lines
4.9 KiB
TypeScript

/**
* Torture fixture — exercises every TS/TSX extraction path the kernel ports.
*/
import React, { forwardRef, memo, useState as useStateAlias } from 'react';
import * as NS from './namespace-module';
import DefaultThing from './default-module';
import './side-effect';
import { helperFn, CONFIG_TABLE } from './helpers';
export { reExported, orig as aliased } from './barrel-source';
export * from './star-source';
// A documented constant table (value-ref target).
const RETRY_LIMITS = { a: 1, b: 2 };
export const API_BASE = 'https://example.test';
let plainLet = 42;
var oldVar = 'x';
/** Class docs.
* Multi-line.
*/
@Injectable()
@scoped.Registry<Config>('name')
export abstract class BaseService extends EventTarget implements Disposable, Serializable {
static instances = 0;
private readonly cache: Map<string, Config> = new Map();
public fonts: FontConfig;
count = 0;
onScroll = throttle((e: Event) => {
this.handleScroll(e);
}, 100);
handleClick = (ev: MouseEvent): void => {
emitTelemetry(ev);
new AbortController();
};
@Get('/list')
async list(query: QueryOpts): Promise<Result<Item>> {
const local: ItemMapper = makeMapper();
const limit = RETRY_LIMITS;
return this.cache.get(query.key) ?? helperFn(query);
}
private static compute(n: number): number {
return n * RETRY_LIMITS.a;
}
get size(): number {
return this.count;
}
protected dispose(): void {
listeners.forEach((l) => unregister(l));
}
}
class Plain {
constructor(private svc: BaseService) {
register(this.onEvent);
btn.on('click', this.handleClick);
}
onEvent() {}
handleClick() {}
}
export interface Shape extends Named, Sized {
area: number;
resize(f: number): Shape;
onchange: (s: Shape) => void;
}
export enum Direction {
Up,
Down = 2,
Left,
}
export type Handle = {
stop: () => void;
id: string;
refresh(force: boolean): Promise<void>;
};
export type ServiceList = [
Service<'query_apply_record', Req, Resp>,
Service<'apply_confirm', Req, Resp>,
];
export type MaybeShape = Shape | null;
export function topLevel(a: ShapeConfig): Shape {
const inner = () => reallyDeep();
function namedInner(): void {
chained(a).value;
}
namedInner();
return makeShape(a);
}
async function* generatorFn(items: Item[]) {
yield* items.map((i) => transform(i));
}
export const arrowConst = async (x: number) => {
return x + plainLet;
};
const AnonClassHolder = class {
method() {}
};
// React components (#841).
export const Button = forwardRef((props: ButtonProps, ref) => {
const [state, setState] = useStateAlias(0);
useEffect(() => {
trackRender();
});
return <BaseButton ref={ref} onClick={() => setState(state + 1)} {...props} />;
});
export const MemoRow = memo(function Row(props: RowProps) {
return <tr className={props.cls}>{props.children}</tr>;
});
export const Wrapped = React.memo(ImportedComponent);
export const Styled = styled.button`
color: red;
`;
const memoCache = memo(computeThing); // lowercase — stays a constant
export function App() {
const nodes: GraphNode[] = [];
return (
<div>
<Button label={CONFIG_TABLE.label} />
<NS.Panel />
{nodes.map((n) => (
<MemoRow key={n.id} cls={n.cls} />
))}
</div>
);
}
// Object-of-functions (SvelteKit-style actions map).
export const actionsMap = {
create: async (input: CreateInput) => {
return persistNew(input);
},
update(input: UpdateInput) {
return persistExisting(input);
},
};
// Zustand-style store through middleware wrappers.
export const useStore = create(
persist(
(set, get) => ({
fetchUser: async (id: string) => {
const user = await api.load(id);
set({ user });
},
reset: () => set({ user: null }),
}),
{ name: 'store' }
)
);
// RTK Query.
export const widgetApi = createApi({
reducerPath: 'widgets',
endpoints: (build) => ({
getWidget: build.query({
query: (id: string) => fetchWidget(id),
}),
updateWidget: build.mutation({
queryFn: async (patch) => {
return applyPatch(patch);
},
}),
prebuilt: build.query(makeEndpointConfig()),
}),
});
export const { useGetWidgetQuery, useUpdateWidgetMutation } = widgetApi;
const { notAHook } = widgetApi;
// Value-ref shadowing: SHADOWED_LIMIT re-bound locally must be pruned.
const SHADOWED_LIMIT = 10;
export function readsShadowed() {
const SHADOWED_LIMIT = 20;
return SHADOWED_LIMIT;
}
export function readsTable() {
return RETRY_LIMITS.b + API_BASE.length;
}
// Fn-ref registrations.
registerHandler(helperFn);
queueMicrotask(topLevel);
const routeTable = { home: topLevel, missing: notDefinedAnywhere };
const handlerList = [helperFn, DefaultThing];
target.cb = topLevel;
const aliasFn = topLevel;
// Calls with interesting callees.
;(topLevel)(cfg);
NS.helper.deep(1);
"literal".includes('x');
[1, 2].map(String);
obj?.optMethod?.(3);
import('./dynamic-module');
new NS.Widget(makeArg());
new Map<string, number>();
super_weird?.();