Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var JsxKeyRule = rule.Rule{ Name: "react/jsx-key", Run: func(ctx rule.RuleContext, rawOptions any) rule.RuleListeners { opts := parseOptions(rawOptions) reactPragma := reactutil.GetReactPragma(ctx.Settings) fragmentPragma := reactutil.GetReactFragmentPragma(ctx.Settings) missingIterKeyUsePragDesc := `Missing "key" prop for element in iterator. Shorthand fragment syntax does not support providing keys. Use ` + reactPragma + `.` + fragmentPragma + ` instead` missingArrayKeyUsePragDesc := `Missing "key" prop for element in array. Shorthand fragment syntax does not support providing keys. Use ` + reactPragma + `.` + fragmentPragma + ` instead` isWithinChildrenToArray := false reportMissingIterKey := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "missingIterKey", Description: `Missing "key" prop for element in iterator`, }) } reportMissingIterKeyUsePrag := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "missingIterKeyUsePrag", Description: missingIterKeyUsePragDesc, }) } reportMissingArrayKey := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "missingArrayKey", Description: `Missing "key" prop for element in array`, }) } reportMissingArrayKeyUsePrag := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "missingArrayKeyUsePrag", Description: missingArrayKeyUsePragDesc, }) } reportKeyBeforeSpread := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "keyBeforeSpread", Description: "`key` prop must be placed before any `{...spread}, to avoid conflicting with React\u2019s new JSX transform: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html`", }) } reportNonUniqueKeys := func(node *ast.Node) { ctx.ReportNode(node, rule.RuleMessage{ Id: "nonUniqueKeys", Description: "`key` prop must be unique", }) } checkIteratorElement := func(node *ast.Node) { if node == nil { return } if isJsxElementLike(node) { attrs := getJsxAttributeProps(node) if !hasKeyAttribute(attrs) { reportMissingIterKey(node) return } if opts.checkKeyMustBeforeSpread && isKeyAfterSpread(attrs) { reportKeyBeforeSpread(node) } return } if opts.checkFragmentShorthand && ast.IsJsxFragment(node) { reportMissingIterKeyUsePrag(node) } } peelToJsxCandidates := func(expr *ast.Node) { expr = ast.SkipParentheses(expr) if expr == nil { return } if ast.IsConditionalExpression(expr) { ce := expr.AsConditionalExpression() if ce.WhenTrue != nil { t := ast.SkipParentheses(ce.WhenTrue) if isJsxNode(t) { checkIteratorElement(t) } } if ce.WhenFalse != nil { f := ast.SkipParentheses(ce.WhenFalse) if isJsxNode(f) { checkIteratorElement(f) } } return } if ast.IsLogicalOrCoalescingBinaryExpression(expr) { right := expr.AsBinaryExpression().Right if right != nil { r := ast.SkipParentheses(right) if isJsxNode(r) { checkIteratorElement(r) } } return } checkIteratorElement(expr) } checkArrowBodyJSX := func(fn *ast.Node) { if !ast.IsArrowFunction(fn) { return } body := fn.AsArrowFunction().Body if body == nil { return } peelToJsxCandidates(body) } checkFunctionsBlockStatement := func(fn *ast.Node) { if !ast.IsFunctionExpressionOrArrowFunction(fn) { return } var body *ast.Node if ast.IsArrowFunction(fn) { body = fn.AsArrowFunction().Body } else { body = fn.AsFunctionExpression().Body } if body == nil || !ast.IsBlock(body) { return } var returns []*ast.Node returns = collectReturnStatements(body, returns) for _, rs := range returns { arg := rs.AsReturnStatement().Expression if arg == nil { continue } peelToJsxCandidates(arg) } } processMapOrFromCallback := func(fn *ast.Node) { fn = ast.SkipParentheses(fn) if !ast.IsFunctionExpressionOrArrowFunction(fn) { return } checkArrowBodyJSX(fn) checkFunctionsBlockStatement(fn) } processArrayLikeSiblings := func(elements []*ast.Node, parentNode *ast.Node, inArray bool) { if len(elements) == 0 { return } var jsxEls []*ast.Node for _, el := range elements { if isJsxElementLike(el) { jsxEls = append(jsxEls, el) } } if len(jsxEls) == 0 { return } var keysByText map[string][]*ast.Node if opts.warnOnDuplicates { keysByText = map[string][]*ast.Node{} } for _, el := range jsxEls { attrs := getJsxAttributeProps(el) keyAttrs := getKeyAttributes(attrs) if len(keyAttrs) == 0 { if inArray { reportMissingArrayKey(el) } continue } for _, k := range keyAttrs { if opts.warnOnDuplicates { text := keyValueText(ctx.SourceFile, k) keysByText[text] = append(keysByText[text], k) } if opts.checkKeyMustBeforeSpread && isKeyAfterSpread(attrs) { reportKeyBeforeSpread(parentNode) } } } for _, group := range keysByText { if len(group) <= 1 { continue } for _, attr := range group { reportNonUniqueKeys(attr) } } } return rule.RuleListeners{ ast.KindCallExpression: func(node *ast.Node) { call := node.AsCallExpression() if isChildrenToArrayCall(call, reactPragma) { isWithinChildrenToArray = true return } if isWithinChildrenToArray { return } switch calleePropertyName(call) { case "map": if call.Arguments == nil || len(call.Arguments.Nodes) == 0 { return } processMapOrFromCallback(call.Arguments.Nodes[0]) case "from": if call.Arguments == nil || len(call.Arguments.Nodes) < 2 { return } processMapOrFromCallback(call.Arguments.Nodes[1]) } }, rule.ListenerOnExit(ast.KindCallExpression): func(node *ast.Node) { if isChildrenToArrayCall(node.AsCallExpression(), reactPragma) { isWithinChildrenToArray = false } }, ast.KindArrayLiteralExpression: func(node *ast.Node) { if isWithinChildrenToArray { return } arr := node.AsArrayLiteralExpression() if arr.Elements == nil { return } processArrayLikeSiblings(arr.Elements.Nodes, node, true) }, ast.KindJsxElement: func(node *ast.Node) { if isWithinChildrenToArray { return } jsxEl := node.AsJsxElement() if jsxEl.Children == nil { return } processArrayLikeSiblings(jsxEl.Children.Nodes, node, false) }, ast.KindJsxFragment: func(node *ast.Node) { if isWithinChildrenToArray || !opts.checkFragmentShorthand { return } parent := node.Parent for parent != nil && ast.IsParenthesizedExpression(parent) { parent = parent.Parent } if parent != nil && ast.IsArrayLiteralExpression(parent) { reportMissingArrayKeyUsePrag(node) } }, } }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.