mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-26 04:19:33 +08:00
fix(react): a typed styled component is a component, not a constant (#2429)
`const Wrapper = styled.div<WrapperProps>` + a template literal came out as a
`constant`, so the JSX-render synthesizer skipped it: `<Wrapper>` got no
render edge, or bound to a same-named component in another file.
tree-sitter's tagged-template call takes no type arguments, so when the type
argument also reads as an expression the initializer parses as comparisons,
`(styled.div < WrapperProps) > template`, not as a call. (A type argument
that can't be an expression, `<{ open: boolean }>`, error-recovers into a
call on the tag, which #841 already took.)
reactComponentHoc now also takes that comparison chain: the leftmost operand
is the `styled` tag against a `<`, the rightmost is the template against a
`>` (`>>` when the type argument ends in its own `<...>`), and operators in
between belong to the type argument (`<A & B>`). Mirrored in the kernel's
TS/JS walker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7a79654157
commit
78f5c8893a
@@ -103,6 +103,9 @@ impl<'t> Walker<'t> {
|
||||
/// inner is the inline render function, or None for `styled.x`/`memo(Ref)`.
|
||||
/// Outer None = not a component wrapper.
|
||||
fn react_component_hoc(&self, value: Node<'t>) -> Option<Option<Node<'t>>> {
|
||||
if self.is_typed_styled_template(value) {
|
||||
return Some(None);
|
||||
}
|
||||
if value.kind() != "call_expression" {
|
||||
return None;
|
||||
}
|
||||
@@ -128,6 +131,35 @@ impl<'t> Walker<'t> {
|
||||
Some(inner)
|
||||
}
|
||||
|
||||
/// A typed styled tag, `styled.div<Props>` before a template literal, which
|
||||
/// parses as comparisons: `(styled.div < Props) > template`. The leftmost
|
||||
/// operand is the `styled` tag against a `<`; the rightmost is the
|
||||
/// template_string against a `>` (`>>` when the type argument ends in its
|
||||
/// own `<…>`). Mirrors TreeSitterExtractor.isTypedStyledTemplate.
|
||||
fn is_typed_styled_template(&self, value: Node<'t>) -> bool {
|
||||
if value.kind() != "binary_expression" {
|
||||
return false;
|
||||
}
|
||||
let (mut tag_parent, mut tag) = (value, value.child_by_field_name("left"));
|
||||
while let Some(t) = tag.filter(|t| t.kind() == "binary_expression") {
|
||||
(tag_parent, tag) = (t, t.child_by_field_name("left"));
|
||||
}
|
||||
let (mut template_parent, mut template) = (value, value.child_by_field_name("right"));
|
||||
while let Some(t) = template.filter(|t| t.kind() == "binary_expression") {
|
||||
(template_parent, template) = (t, t.child_by_field_name("right"));
|
||||
}
|
||||
let (Some(tag), Some(template)) = (tag, template) else {
|
||||
return false;
|
||||
};
|
||||
let operator = |n: Node<'t>| n.child_by_field_name("operator").map(|o| o.kind()).unwrap_or("");
|
||||
let closing = operator(template_parent);
|
||||
template.kind() == "template_string"
|
||||
&& operator(tag_parent) == "<"
|
||||
&& !closing.is_empty()
|
||||
&& closing.bytes().all(|b| b == b'>')
|
||||
&& util::styled_callee().is_match(self.text(tag))
|
||||
}
|
||||
|
||||
fn extract_react_component_node(
|
||||
&mut self,
|
||||
name: &str,
|
||||
|
||||
Reference in New Issue
Block a user