Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var NoDangerWithChildrenRule = rule.Rule{ Name: "react/no-danger-with-children", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { resolveObjectLiteralInit := func(ident *ast.Node) *ast.ObjectLiteralExpression { if ident == nil || ident.Kind != ast.KindIdentifier { return nil } if ctx.TypeChecker == nil { return nil } decl := utils.GetDeclaration(ctx.TypeChecker, ident) if decl == nil || decl.Kind != ast.KindVariableDeclaration { return nil } init := decl.AsVariableDeclaration().Initializer if init == nil { return nil } init = ast.SkipParentheses(init) if init.Kind != ast.KindObjectLiteralExpression { return nil } return init.AsObjectLiteralExpression() } // findObjectPropByName reports whether the object literal has a // property with the given name — either directly (Identifier key, to // match upstream's `prop.key.name === propName`) or via a spread whose // initializer resolves to another object literal. // // `seen` tracks Identifier names already followed through a spread, // so self-referencing initializers like `const props = { ...props }` // terminate instead of looping. // // Upstream intentionally matches ONLY Identifier keys — a // StringLiteral key (`"children": "x"`) has `.value` but no `.name`, // so upstream's `prop.key.name === propName` is always false on it. // We keep parity by not using `utils.GetStaticPropertyName` here. var findObjectPropByName func(obj *ast.ObjectLiteralExpression, name string, seen map[string]bool) bool findObjectPropByName = func(obj *ast.ObjectLiteralExpression, name string, seen map[string]bool) bool { if obj == nil || obj.Properties == nil { return false } for _, prop := range obj.Properties.Nodes { switch prop.Kind { case ast.KindPropertyAssignment: keyNode := prop.AsPropertyAssignment().Name() if keyNode != nil && keyNode.Kind == ast.KindIdentifier && keyNode.AsIdentifier().Text == name { return true } case ast.KindShorthandPropertyAssignment: keyNode := prop.AsShorthandPropertyAssignment().Name() if keyNode != nil && keyNode.Kind == ast.KindIdentifier && keyNode.AsIdentifier().Text == name { return true } case ast.KindSpreadAssignment: expr := ast.SkipParentheses(prop.AsSpreadAssignment().Expression) if expr.Kind != ast.KindIdentifier { continue } spreadName := expr.AsIdentifier().Text if seen[spreadName] { continue } inner := resolveObjectLiteralInit(expr) if inner == nil { continue } nextSeen := make(map[string]bool, len(seen)+1) for k, v := range seen { nextSeen[k] = v } nextSeen[spreadName] = true if findObjectPropByName(inner, name, nextSeen) { return true } } } return false } asPropsObject := func(node *ast.Node) (*ast.ObjectLiteralExpression, map[string]bool) { node = ast.SkipParentheses(node) if node.Kind == ast.KindObjectLiteralExpression { return node.AsObjectLiteralExpression(), map[string]bool{} } if node.Kind == ast.KindIdentifier { if obj := resolveObjectLiteralInit(node); obj != nil { return obj, map[string]bool{node.AsIdentifier().Text: true} } } return nil, nil } findJsxAttr := func(element *ast.Node, name string) bool { for _, attr := range reactutil.GetJsxElementAttributes(element) { switch attr.Kind { case ast.KindJsxAttribute: nameNode := attr.AsJsxAttribute().Name() if nameNode != nil && nameNode.Kind == ast.KindIdentifier && nameNode.AsIdentifier().Text == name { return true } case ast.KindJsxSpreadAttribute: expr := ast.SkipParentheses(attr.AsJsxSpreadAttribute().Expression) if expr.Kind != ast.KindIdentifier { continue } inner := resolveObjectLiteralInit(expr) if inner == nil { continue } seen := map[string]bool{expr.AsIdentifier().Text: true} if findObjectPropByName(inner, name, seen) { return true } } } return false } isLineBreak := func(child *ast.Node) bool { if child == nil || child.Kind != ast.KindJsxText { return false } text := child.AsJsxText().Text if strings.TrimSpace(text) != "" { return false } return strings.Contains(text, "\n") } checkJsx := func(elementForAttrs, reportNode *ast.Node, firstChild *ast.Node) { hasChildren := false if firstChild != nil && !isLineBreak(firstChild) { hasChildren = true } else if findJsxAttr(elementForAttrs, "children") { hasChildren = true } if !hasChildren { return } if findJsxAttr(elementForAttrs, "dangerouslySetInnerHTML") { ctx.ReportNode(reportNode, rule.RuleMessage{ Id: "dangerWithChildren", Description: dangerWithChildrenMessage, }) } } return rule.RuleListeners{ ast.KindJsxElement: func(node *ast.Node) { jsx := node.AsJsxElement() var firstChild *ast.Node if jsx.Children != nil && len(jsx.Children.Nodes) > 0 { firstChild = jsx.Children.Nodes[0] } checkJsx(jsx.OpeningElement, node, firstChild) }, ast.KindJsxSelfClosingElement: func(node *ast.Node) { checkJsx(node, node, nil) }, ast.KindCallExpression: func(node *ast.Node) { call := node.AsCallExpression() if !isCreateElementCallee(call.Expression) { return } if call.Arguments == nil || len(call.Arguments.Nodes) < 2 { return } obj, seen := asPropsObject(call.Arguments.Nodes[1]) if obj == nil { return } if !findObjectPropByName(obj, "dangerouslySetInnerHTML", seen) { return } hasChildren := len(call.Arguments.Nodes) > 2 if !hasChildren { hasChildren = findObjectPropByName(obj, "children", seen) } if hasChildren { ctx.ReportNode(node, rule.RuleMessage{ Id: "dangerWithChildren", Description: dangerWithChildrenMessage, }) } }, } }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.