Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var DestructuringAssignmentRule = rule.Rule{ Name: "react/destructuring-assignment", Run: func(ctx rule.RuleContext, options any) rule.RuleListeners { opts := parseOptions(options) pragma := reactutil.GetReactPragma(ctx.Settings) createClass := reactutil.GetReactCreateClass(ctx.Settings) wrappers := reactutil.GetComponentWrapperFunctions(ctx.Settings, pragma) stack := &sfcParamsStack{} reportUseDestruct := func(node *ast.Node, t string) { ctx.ReportNode(node, rule.RuleMessage{ Id: "useDestructAssignment", Description: "Must use destructuring " + t + " assignment", Data: map[string]string{"type": t}, }) } reportNoDestruct := func(node *ast.Node, t string) { ctx.ReportNode(node, rule.RuleMessage{ Id: "noDestructAssignment", Description: "Must never use destructuring " + t + " assignment", Data: map[string]string{"type": t}, }) } handleStatelessComponent := func(node *ast.Node) { if !reactutil.IsStatelessReactComponentWithWrappers(node, pragma, nil, wrappers) { return } params := evalParams(reactutil.FunctionParameters(node), ctx.TypeChecker) stack.push(params) if opts.configuration != "never" { return } if len(params) > 0 && params[0].destructuring { ctx.ReportNode(node, rule.RuleMessage{ Id: "noDestructPropsInSFCArg", Description: "Must never use destructuring props assignment in SFC argument", }) } else if len(params) > 1 && params[1].destructuring { ctx.ReportNode(node, rule.RuleMessage{ Id: "noDestructContextInSFCArg", Description: "Must never use destructuring context assignment in SFC argument", }) } } handleStatelessComponentExit := func(node *ast.Node) { if !reactutil.IsStatelessReactComponentWithWrappers(node, pragma, nil, wrappers) { return } stack.pop() } matchesSFCParam := buildSFCMatcher(ctx.TypeChecker) handleSFCUsage := func(node *ast.Node) { propsName := stack.propsName() contextName := stack.contextName() var objNode *ast.Node switch node.Kind { case ast.KindPropertyAccessExpression: objNode = node.AsPropertyAccessExpression().Expression case ast.KindElementAccessExpression: objNode = node.AsElementAccessExpression().Expression default: return } obj := ast.SkipParentheses(objNode) if obj.Kind != ast.KindIdentifier { return } objName := obj.AsIdentifier().Text matched := false if propsName != "" && objName == propsName && matchesSFCParam(obj, stack.propsSymbol()) { matched = true } else if contextName != "" && objName == contextName && matchesSFCParam(obj, stack.contextSymbol()) { matched = true } if !matched { return } if isAssignmentLHS(node) { return } if opts.configuration != "always" || isOptionalMember(node) { return } reportUseDestruct(node, objName) } handleClassUsage := func(node *ast.Node) { var objNode *ast.Node switch node.Kind { case ast.KindPropertyAccessExpression: objNode = node.AsPropertyAccessExpression().Expression case ast.KindElementAccessExpression: objNode = node.AsElementAccessExpression().Expression default: return } obj := ast.SkipParentheses(objNode) if obj.Kind != ast.KindPropertyAccessExpression { return } inner := obj.AsPropertyAccessExpression() if ast.SkipParentheses(inner.Expression).Kind != ast.KindThisKeyword { return } name := reactutil.EsTreeName(inner.Name()) if name != "props" && name != "state" && name != "context" { return } if isAssignmentLHS(node) { return } if opts.configuration != "always" { return } if opts.ignoreClassFields && isInClassProperty(node) { return } reportUseDestruct(node, name) } memberExprListener := func(node *ast.Node) { if reactutil.GetParentStatelessComponent(node, pragma, wrappers) != nil { handleSFCUsage(node) } if reactutil.GetParentReactComponentScopeBasedOrStateless(node, pragma, createClass, wrappers) != nil { handleClassUsage(node) } } return rule.RuleListeners{ ast.KindFunctionDeclaration: handleStatelessComponent, ast.KindFunctionExpression: handleStatelessComponent, ast.KindArrowFunction: handleStatelessComponent, ast.KindMethodDeclaration: handleStatelessComponent, rule.ListenerOnExit(ast.KindFunctionDeclaration): handleStatelessComponentExit, rule.ListenerOnExit(ast.KindFunctionExpression): handleStatelessComponentExit, rule.ListenerOnExit(ast.KindArrowFunction): handleStatelessComponentExit, rule.ListenerOnExit(ast.KindMethodDeclaration): handleStatelessComponentExit, ast.KindPropertyAccessExpression: memberExprListener, ast.KindElementAccessExpression: memberExprListener, ast.KindQualifiedName: func(node *ast.Node) { if opts.configuration != "always" { return } qn := node.AsQualifiedName() if qn.Left == nil || qn.Left.Kind != ast.KindIdentifier { return } propsName := stack.propsName() if propsName == "" || qn.Left.AsIdentifier().Text != propsName { return } if !findEnclosingTypeQuery(node) { return } if reactutil.GetParentStatelessComponent(node, pragma, wrappers) == nil { return } reportUseDestruct(node, "props") }, ast.KindVariableDeclaration: func(node *ast.Node) { vd := node.AsVariableDeclaration() if vd.Initializer == nil { return } name := vd.Name() if name == nil || name.Kind != ast.KindObjectBindingPattern { return } init := ast.SkipParentheses(vd.Initializer) var sfcType string var classType string switch init.Kind { case ast.KindIdentifier: nm := init.AsIdentifier().Text if nm == "props" || nm == "context" { sfcType = nm } case ast.KindPropertyAccessExpression: pa := init.AsPropertyAccessExpression() if ast.SkipParentheses(pa.Expression).Kind == ast.KindThisKeyword { propName := reactutil.EsTreeName(pa.Name()) if propName == "props" || propName == "context" || propName == "state" { classType = propName } } } sfcComp := getEnclosingSFCComponent(node, pragma, wrappers) classComp := reactutil.GetParentReactComponentScopeBasedOrStateless(node, pragma, createClass, wrappers) if opts.configuration == "never" { if sfcComp != nil && sfcType != "" { reportNoDestruct(node, sfcType) } if classComp != nil && classType != "" { if !opts.ignoreClassFields || !isParentClassProperty(node) { reportNoDestruct(node, classType) } } } if sfcComp != nil && sfcType == "props" && opts.configuration == "always" && opts.destructureInSignature == "always" { params := reactutil.FunctionParameters(sfcComp) if len(params) == 0 { return } param := params[0] if param == nil || param.Kind != ast.KindParameter { return } pd := param.AsParameterDeclaration() paramName := pd.Name() if paramName == nil || paramName.Kind != ast.KindIdentifier || paramName.AsIdentifier().Text != "props" { return } if countPropsRefsExcludingDecl(sfcComp, vd.Initializer, ctx.TypeChecker, paramName) > 0 { return } replaceRange := utils.TrimNodeTextRange(ctx.SourceFile, paramName) patternText := utils.TrimmedNodeText(ctx.SourceFile, name) removeTarget := node if node.Parent != nil && node.Parent.Kind == ast.KindVariableDeclarationList { list := node.Parent if list.Parent != nil && list.Parent.Kind == ast.KindVariableStatement { removeTarget = list.Parent } } ctx.ReportNodeWithFixes(node, rule.RuleMessage{ Id: "destructureInSignature", Description: "Must destructure props in the function signature.", }, rule.RuleFixReplaceRange(replaceRange, patternText), rule.RuleFixRemove(ctx.SourceFile, removeTarget), ) } }, } }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.