a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
package languages
|
|
|
|
import (
|
|
sitter "github.com/zzet/gortex/internal/parser/tsitter"
|
|
)
|
|
|
|
// React higher-order-component classification. A PascalCase const assigned
|
|
// `memo(...)`, `forwardRef(...)`, `React.memo(...)`, `styled.tag`...`` or
|
|
// `styled(Base)`...`` is a component, not a plain variable — but the TS/JS
|
|
// extractor mints it as a bare KindVariable. reactHOCComponentKind recognizes
|
|
// the initializer shape so the variable visitor can stamp Meta["component"]
|
|
// and re-attribute the inline render function's JSX to the outer const.
|
|
|
|
// reactHOCComponentKind classifies a variable_declarator's initializer as a
|
|
// React HOC component. It returns the component_kind ("memo" / "forwardRef" /
|
|
// "styled") and, for memo/forwardRef, the inline render function whose body's
|
|
// JSX should be attributed to the outer component (nil for styled, whose
|
|
// argument is a CSS template). Returns "" when the initializer is not a HOC.
|
|
func reactHOCComponentKind(node *sitter.Node, src []byte) (kind string, renderFn *sitter.Node) {
|
|
declarator := node
|
|
if declarator != nil && declarator.Type() != "variable_declarator" {
|
|
// The variable visitor passes the enclosing lexical_declaration; descend
|
|
// to its declarator.
|
|
declarator = firstChildOfType(node, "variable_declarator")
|
|
}
|
|
if declarator == nil {
|
|
return "", nil
|
|
}
|
|
val := declarator.ChildByFieldName("value")
|
|
if val == nil || val.Type() != "call_expression" {
|
|
return "", nil
|
|
}
|
|
fn := val.ChildByFieldName("function")
|
|
if fn == nil {
|
|
return "", nil
|
|
}
|
|
switch fn.Type() {
|
|
case "identifier":
|
|
switch fn.Content(src) {
|
|
case "memo":
|
|
return "memo", reactHOCRenderFn(val)
|
|
case "forwardRef":
|
|
return "forwardRef", reactHOCRenderFn(val)
|
|
}
|
|
case "member_expression":
|
|
obj := fn.ChildByFieldName("object")
|
|
if obj != nil && obj.Content(src) == "styled" {
|
|
return "styled", nil // styled.div`...`
|
|
}
|
|
if obj != nil && obj.Content(src) == "React" {
|
|
if prop := fn.ChildByFieldName("property"); prop != nil {
|
|
switch prop.Content(src) {
|
|
case "memo":
|
|
return "memo", reactHOCRenderFn(val)
|
|
case "forwardRef":
|
|
return "forwardRef", reactHOCRenderFn(val)
|
|
}
|
|
}
|
|
}
|
|
case "call_expression":
|
|
// styled(Base)`...` — a tagged template whose tag is a styled() call.
|
|
if inner := fn.ChildByFieldName("function"); inner != nil && inner.Content(src) == "styled" {
|
|
return "styled", nil
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// reactHOCRenderFn returns the first argument of a call when it is an inline
|
|
// arrow / function expression (the HOC's render function), else nil.
|
|
func reactHOCRenderFn(call *sitter.Node) *sitter.Node {
|
|
args := call.ChildByFieldName("arguments")
|
|
if args == nil {
|
|
return nil
|
|
}
|
|
for i, _nc := 0, int(args.NamedChildCount()); i < _nc; i++ {
|
|
a := args.NamedChild(i)
|
|
if a == nil {
|
|
continue
|
|
}
|
|
switch a.Type() {
|
|
case "arrow_function", "function_expression", "function":
|
|
return a
|
|
}
|
|
return nil // first argument is not an inline function
|
|
}
|
|
return nil
|
|
}
|