no_misused_spread

package
v0.1.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2025 License: MIT Imports: 6 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var NoMisusedSpreadRule = rule.CreateRule(rule.Rule{
	Name: "no-misused-spread",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		opts, ok := options.(NoMisusedSpreadOptions)
		if !ok {
			opts = NoMisusedSpreadOptions{}
		}
		if opts.Allow == nil {
			opts.Allow = []utils.TypeOrValueSpecifier{}
		}
		if opts.AllowInline == nil {
			opts.AllowInline = []string{}
		}

		checkArrayOrCallSpread := func(node *ast.Node) {
			t := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, node.AsSpreadElement().Expression)
			if !utils.TypeMatchesSomeSpecifier(t, opts.Allow, opts.AllowInline, ctx.Program) && isString(t) {
				ctx.ReportNode(node, buildNoStringSpreadMessage())
			}
		}

		insertAwaitFix := func(node *ast.Node) []rule.RuleFix {
			if utils.IsHigherPrecedenceThanAwait(node) {
				return []rule.RuleFix{
					rule.RuleFixInsertBefore(ctx.SourceFile, node, "await "),
				}
			}
			return []rule.RuleFix{
				rule.RuleFixInsertBefore(ctx.SourceFile, node, "await ("),
				rule.RuleFixInsertAfter(node, ")"),
			}
		}

		getMapSpreadSuggestions := func(node *ast.Node, argument *ast.Node, t *checker.Type) []rule.RuleSuggestion {

			for _, t := range utils.UnionTypeParts(t) {
				if !isMap(ctx.Program, ctx.TypeChecker, t) {
					return []rule.RuleSuggestion{}
				}
			}

			if ast.IsObjectLiteralExpression(node.Parent) {
				properties := node.Parent.AsObjectLiteralExpression().Properties
				if len(properties.Nodes) == 1 {
					return []rule.RuleSuggestion{
						{
							Message: buildReplaceMapSpreadInObjectMessage(),
							FixesArr: []rule.RuleFix{
								rule.RuleFixRemoveRange(scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, node.Parent.Pos())),
								rule.RuleFixReplaceRange(scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, node.Pos()), "Object.fromEntries("),
								rule.RuleFixReplaceRange(properties.Loc.WithPos(argument.End()), ")"),
								rule.RuleFixRemoveRange(node.Parent.Loc.WithPos(node.Parent.End() - 1)),
							},
						},
					}
				}
			}

			return []rule.RuleSuggestion{
				{
					Message: buildReplaceMapSpreadInObjectMessage(),
					FixesArr: []rule.RuleFix{
						rule.RuleFixInsertBefore(ctx.SourceFile, argument, "Object.fromEntries("),
						rule.RuleFixInsertAfter(argument, ")"),
					},
				},
			}
		}

		checkObjectSpread := func(node *ast.Node, argument *ast.Node) {
			t := utils.GetConstrainedTypeAtLocation(ctx.TypeChecker, argument)

			if utils.TypeMatchesSomeSpecifier(t, opts.Allow, opts.AllowInline, ctx.Program) {
				return
			}

			if isPromise(ctx.Program, ctx.TypeChecker, t) {
				ctx.ReportNodeWithSuggestions(node, buildNoPromiseSpreadInObjectMessage(), rule.RuleSuggestion{
					Message:  buildAddAwaitMessage(),
					FixesArr: insertAwaitFix(ast.SkipParentheses(argument)),
				})

				return
			}

			if isFunctionWithoutProps(ctx.TypeChecker, t) {
				ctx.ReportNode(node, buildNoFunctionSpreadInObjectMessage())

				return
			}

			if isMap(ctx.Program, ctx.TypeChecker, t) {
				ctx.ReportNodeWithSuggestions(node, buildNoMapSpreadInObjectMessage(), getMapSpreadSuggestions(node, argument, t)...)

				return
			}

			if isArray(ctx.TypeChecker, t) {
				ctx.ReportNode(node, buildNoArraySpreadInObjectMessage())

				return
			}

			if isIterable(ctx.TypeChecker, t) && !isString(t) {
				ctx.ReportNode(node, buildNoIterableSpreadInObjectMessage())

				return
			}

			if isClassInstance(ctx.TypeChecker, t) {
				ctx.ReportNode(node, buildNoClassInstanceSpreadInObjectMessage())

				return
			}

			if isClassDeclaration(t) {
				ctx.ReportNode(node, buildNoClassDeclarationSpreadInObjectMessage())

				return
			}
		}

		return rule.RuleListeners{
			rule.ListenerOnNotAllowPattern(ast.KindArrayLiteralExpression): func(node *ast.Node) {
				for _, element := range node.AsArrayLiteralExpression().Elements.Nodes {
					if ast.IsSpreadElement(element) {
						checkArrayOrCallSpread(element)
					}
				}
			},
			ast.KindCallExpression: func(node *ast.Node) {
				for _, element := range node.AsCallExpression().Arguments.Nodes {
					if ast.IsSpreadElement(element) {
						checkArrayOrCallSpread(element)
					}
				}
			},
			ast.KindJsxSpreadAttribute: func(node *ast.Node) {
				checkObjectSpread(node, node.AsJsxSpreadAttribute().Expression)
			},
			rule.ListenerOnNotAllowPattern(ast.KindObjectLiteralExpression): func(node *ast.Node) {
				for _, element := range node.AsObjectLiteralExpression().Properties.Nodes {
					if ast.IsSpreadAssignment(element) {
						checkObjectSpread(element, element.AsSpreadAssignment().Expression)
					}
				}
			},
		}
	},
})

Functions

This section is empty.

Types

type NoMisusedSpreadOptions

type NoMisusedSpreadOptions struct {
	Allow       []utils.TypeOrValueSpecifier
	AllowInline []string
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL