Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3892e7d
chore: gitignore napi-generated artifacts in crates/codegraph-core
carlos-alm Jun 13, 2026
ef8ea4f
chore(tests): remove unused biome suppression in visitor.test.ts
carlos-alm Jun 13, 2026
a372b82
fix(titan-run): sync --start-from enum and phase-timestamp list with …
carlos-alm Jun 13, 2026
9a52c7c
fix(hooks): track Bash file modifications via before/after git status…
carlos-alm Jun 13, 2026
85a26df
chore(native): remove dead code (unused var, method, variant, fields)
carlos-alm Jun 13, 2026
184d221
refactor(native): extract emit_pts_alias_edges params into PtsAliasCt…
carlos-alm Jun 13, 2026
909e1df
fix(wasm): sort call targets by confidence before emit to match nativ…
carlos-alm Jun 13, 2026
66fc899
fix(bench): add 2 warmup runs and raise INCREMENTAL_RUNS to 5 for inc…
carlos-alm Jun 13, 2026
84e1a5f
ci(bench): add per-PR perf canary for extractor/graph/native changes
carlos-alm Jun 13, 2026
d07b358
fix(perf): plumb symbolsOnly through parseFilesWasmInline to skip ana…
carlos-alm Jun 13, 2026
3db5d8c
fix(perf): scope runPostNativeCha to changed files on incremental builds
carlos-alm Jun 13, 2026
8b3aa3d
fix(native): add post-pass phase timings to result.phases
carlos-alm Jun 13, 2026
fd4ffd1
fix(perf): correct INLINE_BACKFILL_THRESHOLD docstring; raise thresho…
carlos-alm Jun 13, 2026
498ee21
fix(perf): guard post-native passes against unnecessary work on 1-fil…
carlos-alm Jun 13, 2026
61a9839
chore(types): remove dead protoMethodsMs field and stale comment
carlos-alm Jun 13, 2026
5f5d4d2
fix: class-scope field annotation typeMap keys to prevent cross-class…
carlos-alm Jun 13, 2026
29dd101
fix(bench): update elixir/julia/objc expected-edges to module-qualifi…
carlos-alm Jun 13, 2026
9320ed2
fix(wasm): emit receiver edges for declaration-typed locals in C++/CUDA
carlos-alm Jun 13, 2026
7313330
fix(native): resolve Go factory and Python constructor receiver types…
carlos-alm Jun 13, 2026
f93d0cb
fix: align enclosing-caller attribution for variable bindings (haskel…
carlos-alm Jun 13, 2026
1915e5e
chore(lint): fix unused import and formatting in cpp/cuda extractors …
carlos-alm Jun 13, 2026
7095ffe
fix: align Java interface dispatch across wasm/native/hybrid
carlos-alm Jun 13, 2026
1875c7f
fix(wasm): align typed-receiver CHA dispatch confidence to 0.8
carlos-alm Jun 13, 2026
d87c1ee
fix(caller): use nullish coalescing for endLine to avoid treating 0 a…
carlos-alm Jun 13, 2026
7509e4f
fix(wasm): port missing node extractions to JS extractor
carlos-alm Jun 13, 2026
ca77237
fix(extractor): use setTypeMapEntry (first-wins) in C++/CUDA declarat…
carlos-alm Jun 13, 2026
0f5d063
fix: resolve merge conflicts with main
carlos-alm Jun 13, 2026
a71d366
Merge remote-tracking branch 'origin/main' into fix/js-extractor-node…
carlos-alm Jun 13, 2026
fe4fe37
Merge remote-tracking branch 'origin/main' into fix/js-extractor-node…
carlos-alm Jun 13, 2026
c388e08
fix: resolve merge conflicts with main
carlos-alm Jun 13, 2026
9840bb2
fix: resolve merge conflicts with main
carlos-alm Jun 14, 2026
e490b8f
fix: resolve merge conflicts with main
carlos-alm Jun 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/domain/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ const COMMON_QUERY_PATTERNS: string[] = [
'(variable_declarator name: (identifier) @varfn_name value: (generator_function) @varfn_value)',
'(method_definition name: (property_identifier) @meth_name) @meth_node',
'(method_definition name: (private_property_identifier) @meth_name) @meth_node',
'(method_definition name: (computed_property_name) @meth_name) @meth_node',
'(import_statement source: (string) @imp_source) @imp_node',
'(export_statement) @exp_node',
'(call_expression function: (identifier) @callfn_name) @callfn_node',
Expand Down
11 changes: 9 additions & 2 deletions src/domain/wasm-worker-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const COMMON_QUERY_PATTERNS: string[] = [
'(variable_declarator name: (identifier) @varfn_name value: (generator_function) @varfn_value)',
'(method_definition name: (property_identifier) @meth_name) @meth_node',
'(method_definition name: (private_property_identifier) @meth_name) @meth_node',
'(method_definition name: (computed_property_name) @meth_name) @meth_node',
'(import_statement source: (string) @imp_source) @imp_node',
'(export_statement) @exp_node',
'(call_expression function: (identifier) @callfn_name) @callfn_node',
Expand All @@ -125,11 +126,17 @@ const COMMON_QUERY_PATTERNS: string[] = [
'(expression_statement (assignment_expression left: (member_expression) @assign_left right: (_) @assign_right)) @assign_node',
];

const JS_CLASS_PATTERN: string = '(class_declaration name: (identifier) @cls_name) @cls_node';
const JS_CLASS_PATTERNS: string[] = [
'(class_declaration name: (identifier) @cls_name) @cls_node',
// class expressions: `return class Foo extends Bar { ... }` or `const X = class Foo { ... }`
'(class name: (identifier) @cls_name) @cls_node',
];

const TS_EXTRA_PATTERNS: string[] = [
'(class_declaration name: (type_identifier) @cls_name) @cls_node',
'(abstract_class_declaration name: (type_identifier) @cls_name) @cls_node',
// class expressions: `return class Foo extends Bar { ... }`
'(class name: (type_identifier) @cls_name) @cls_node',
'(interface_declaration name: (type_identifier) @iface_name) @iface_node',
'(type_alias_declaration name: (type_identifier) @type_name) @type_node',
];
Expand Down Expand Up @@ -433,7 +440,7 @@ async function loadLanguageLazy(entry: LanguageRegistryEntry): Promise<Parser |
const isTS = entry.id === 'typescript' || entry.id === 'tsx';
const patterns = isTS
? [...COMMON_QUERY_PATTERNS, ...TS_EXTRA_PATTERNS]
: [...COMMON_QUERY_PATTERNS, JS_CLASS_PATTERN];
: [...COMMON_QUERY_PATTERNS, ...JS_CLASS_PATTERNS];
_queries.set(entry.id, new Query(lang, patterns.join('\n')));
}
return parser;
Expand Down
21 changes: 21 additions & 0 deletions src/extractors/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,15 @@ function extractDestructuredBindingsWalk(node: TreeSitterNode, definitions: Defi
nodeEndLine(declNode),
definitions,
);
} else if (nameN && nameN.type === 'array_pattern') {
// `const [x, y] = ...` — emit a single constant node whose name is the
// full array pattern text (e.g. `[x, y]`), matching native engine behaviour.
definitions.push({
name: nameN.text,
kind: 'constant',
line: nodeStartLine(declNode),
endLine: nodeEndLine(declNode),
});
}
}
}
Expand Down Expand Up @@ -1017,6 +1026,16 @@ function handleVariableDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
nodeEndLine(node),
ctx.definitions,
);
} else if (isConst && nameN.type === 'array_pattern' && !hasFunctionScopeAncestor(node)) {
// Array destructuring: `const [x, y] = ...` — emit a single constant node
// whose name is the full array pattern text (e.g. `[x, y]`), matching
// native engine behaviour. Scope guard mirrors the object_pattern branch above.
ctx.definitions.push({
name: nameN.text,
kind: 'constant',
line: nodeStartLine(node),
endLine: nodeEndLine(node),
});
}
}
}
Expand Down Expand Up @@ -3359,11 +3378,13 @@ function emitPrototypeMethod(
): void {
const fullName = `${className}.${methodName}`;
if (rhs.type === 'function_expression' || rhs.type === 'arrow_function') {
const params = extractParameters(rhs);
definitions.push({
name: fullName,
kind: 'method',
line: nodeStartLine(rhs),
endLine: nodeEndLine(rhs),
children: params.length > 0 ? params : undefined,
});
} else if (rhs.type === 'identifier' && !BUILTIN_GLOBALS.has(rhs.text)) {
// Prototype alias: `A.prototype.t = f` → typeMap['A.t'] = { type: 'f' }
Expand Down
114 changes: 114 additions & 0 deletions tests/parsers/javascript.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1265,4 +1265,118 @@ describe('JavaScript parser', () => {
);
});
});

describe('computed method name extraction (#1471)', () => {
it('extracts computed getter method from object literal', () => {
const symbols = parseJS(`const obj = { get ['property7']() {} };`);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: "['property7']", kind: 'method' }),
);
});

it('extracts computed setter method with parameter from object literal', () => {
const symbols = parseJS(`const obj = { set ['property8'](value) {} };`);
const def = symbols.definitions.find((d) => d.name === "['property8']");
expect(def).toBeDefined();
expect(def).toMatchObject({ kind: 'method' });
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'value', kind: 'parameter' }),
);
});

it('extracts computed regular method with parameter from object literal', () => {
const symbols = parseJS(`const obj = { ['property9'](parameters) {} };`);
const def = symbols.definitions.find((d) => d.name === "['property9']");
expect(def).toBeDefined();
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'parameters', kind: 'parameter' }),
);
});

it('extracts computed generator method from object literal', () => {
const symbols = parseJS(`const obj = { *['generator10'](parameters) {} };`);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: "['generator10']", kind: 'method' }),
);
});

it('extracts computed async method from object literal', () => {
const symbols = parseJS(`const obj = { async ['property11'](parameters) {} };`);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: "['property11']", kind: 'method' }),
);
});
});

describe('class expression inside function extraction (#1471)', () => {
it('extracts named class expression returned from a function', () => {
const symbols = parseJS(
`function mixin() { return class PostMixin extends A { constructor() { super(); } }; }`,
);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: 'PostMixin', kind: 'class' }),
);
});

it('records extends relationship for class expression inside function', () => {
const symbols = parseJS(`function mixin() { return class PostMixin extends A { m() {} }; }`);
expect(symbols.classes).toContainEqual(
expect.objectContaining({ name: 'PostMixin', extends: 'A' }),
);
});

it('extracts class field properties as children of class expression', () => {
const symbols = parseJS(
`function mixin() { return class PostMixin extends A { w = 1; eee = this; }; }`,
);
const pm = symbols.definitions.find((d) => d.name === 'PostMixin');
expect(pm).toBeDefined();
expect(pm!.children).toContainEqual(expect.objectContaining({ name: 'w', kind: 'property' }));
expect(pm!.children).toContainEqual(
expect.objectContaining({ name: 'eee', kind: 'property' }),
);
});
});

describe('array destructuring constant extraction (#1471)', () => {
it('extracts const array pattern as a single constant node', () => {
const symbols = parseJS(`const [x, y] = new Set([() => {}, () => {}]);`);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: '[x, y]', kind: 'constant' }),
);
});

it('does not extract let or var array destructuring', () => {
const symbols = parseJS(`let [a, b] = [1, 2];`);
expect(symbols.definitions.every((d) => d.name !== '[a, b]')).toBe(true);
});
});

describe('prototype method parameter extraction (#1471)', () => {
it('extracts parameters from Foo.prototype.bar = (x, y) => arrow', () => {
const symbols = parseJS(`function Arit() {}\nArit.prototype.sum = (x, y) => x + y;`);
const def = symbols.definitions.find((d) => d.name === 'Arit.sum');
expect(def).toBeDefined();
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'x', kind: 'parameter' }),
);
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'y', kind: 'parameter' }),
);
});

it('extracts parameters from Foo.prototype.bar = function(key, value)', () => {
const symbols = parseJS(
`function Foo() {}\nFoo.prototype.add = function(key, value) { this[key] = value; };`,
);
const def = symbols.definitions.find((d) => d.name === 'Foo.add');
expect(def).toBeDefined();
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'key', kind: 'parameter' }),
);
expect(def!.children).toContainEqual(
expect.objectContaining({ name: 'value', kind: 'parameter' }),
);
});
});
});
Loading