Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
72 changes: 68 additions & 4 deletions src/codegraphcontext/tools/languages/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,77 @@ def _extract_parameters(self, params_node):
left_child = child.child_by_field_name('left')
if left_child and left_child.type == 'identifier':
params.append(self._get_node_text(left_child))
elif child.type == 'rest_pattern':
elif child.type in ('rest_element', 'rest_pattern'):
# Rest parameter: ...args
argument = child.child_by_field_name('argument')
if argument and argument.type == 'identifier':
params.append(f"...{self._get_node_text(argument)}")
# Try named field first, then direct identifier child
name_node = (child.child_by_field_name('name') or
child.child_by_field_name('argument'))
if name_node is None:
name_node = next(
(c for c in child.children if c.type == 'identifier'), None
)
if name_node:
params.append(f"...{self._get_node_text(name_node)}")
elif child.type == 'object_pattern':
# Destructured object: {a}, {a: renamed}, {a = 1}
names = self._pattern_binding_names(child)
params.append("{" + ", ".join(names) + "}" if names else "{...}")
elif child.type == 'array_pattern':
# Destructured array: [a, b]
names = self._pattern_binding_names(child)
params.append("[" + ", ".join(names) + "]" if names else "[...]")
return params

def _pattern_binding_names(self, pattern_node):
"""Local names a destructuring pattern binds, in source order.

Recorded rather than collapsed to a placeholder because the binding is
what later code refers to — `function Button({label})` is the dominant
React idiom, and `label` is the name that appears in the body. One
entry is still emitted per *parameter position* so arity, which call
resolution matches on, is unchanged.
"""
names = []

def walk(node):
for child in node.named_children:
if child.type == 'shorthand_property_identifier_pattern':
# { a }
names.append(self._get_node_text(child))
elif child.type == 'pair_pattern':
# { a: local } — the binding is the value, not the key
value = child.child_by_field_name('value')
if value is not None and value.type == 'identifier':
names.append(self._get_node_text(value))
elif value is not None:
walk(value) # nested destructuring
elif child.type == 'object_assignment_pattern':
# { a = 1 }
target = child.child_by_field_name('left') or (
child.named_children[0] if child.named_children else None
)
if target is not None:
if target.type in (
'identifier', 'shorthand_property_identifier_pattern'
):
names.append(self._get_node_text(target))
else:
walk(target)
elif child.type == 'identifier':
# [ a, b ]
names.append(self._get_node_text(child))
elif child.type in ('object_pattern', 'array_pattern'):
walk(child)
elif child.type in ('rest_element', 'rest_pattern'):
inner = next(
(c for c in child.children if c.type == 'identifier'), None
)
if inner is not None:
names.append(f"...{self._get_node_text(inner)}")

walk(pattern_node)
return names


def _get_jsdoc_comment(self, func_node):
"""Extract JSDoc comment preceding the function."""
Expand Down
Loading
Loading