array_type

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: 4 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ArrayTypeRule = rule.CreateRule(rule.Rule{
	Name: "array-type",
	Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
		opts := ArrayTypeOptions{
			Default: "array",
		}

		if options != nil {
			var optsMap map[string]interface{}
			var ok bool

			if optArray, isArray := options.([]interface{}); isArray && len(optArray) > 0 {
				optsMap, ok = optArray[0].(map[string]interface{})
			} else {

				optsMap, ok = options.(map[string]interface{})
			}

			if ok {
				if defaultVal, ok := optsMap["default"].(string); ok {
					opts.Default = defaultVal
				}
				if readonlyVal, ok := optsMap["readonly"].(string); ok {
					opts.Readonly = readonlyVal
				}
			}
		}

		defaultOption := opts.Default
		readonlyOption := opts.Readonly
		if readonlyOption == "" {
			readonlyOption = defaultOption
		}

		getMessageType := func(node *ast.Node) string {
			if isSimpleType(node) {
				nodeRange := utils.TrimNodeTextRange(ctx.SourceFile, node)
				return ctx.SourceFile.Text()[nodeRange.Pos():nodeRange.End()]
			}
			return "T"
		}

		return rule.RuleListeners{
			ast.KindArrayType: func(node *ast.Node) {
				arrayType := node.AsArrayTypeNode()
				if arrayType == nil {
					return
				}

				isReadonly := false
				if node.Parent != nil && node.Parent.Kind == ast.KindTypeOperator {
					typeOp := node.Parent.AsTypeOperatorNode()
					if typeOp != nil {
						isReadonly = typeOp.Operator == ast.KindReadonlyKeyword
					}
				}

				currentOption := defaultOption
				if isReadonly {
					currentOption = readonlyOption
				}

				if currentOption == "array" ||
					(currentOption == "array-simple" && isSimpleType(arrayType.ElementType)) {
					return
				}

				var messageId string
				if currentOption == "generic" {
					messageId = "errorStringGeneric"
				} else {
					messageId = "errorStringGenericSimple"
				}

				errorNode := node
				if isReadonly {
					errorNode = node.Parent
				}

				typeStr := getMessageType(arrayType.ElementType)
				className := "Array"
				readonlyPrefix := ""
				if isReadonly {
					className = "ReadonlyArray"
					readonlyPrefix = "readonly "
				}

				var message rule.RuleMessage
				if messageId == "errorStringGeneric" {
					message = buildErrorStringGenericMessage(readonlyPrefix, typeStr, className)
				} else {
					message = buildErrorStringGenericSimpleMessage(readonlyPrefix, typeStr, className)
				}

				elementTypeRange := utils.TrimNodeTextRange(ctx.SourceFile, arrayType.ElementType)
				elementTypeText := ctx.SourceFile.Text()[elementTypeRange.Pos():elementTypeRange.End()]

				if ast.IsParenthesizedTypeNode(arrayType.ElementType) {

					parenType := arrayType.ElementType.AsParenthesizedTypeNode()
					if parenType != nil && parenType.Type != nil {
						innerTypeRange := utils.TrimNodeTextRange(ctx.SourceFile, parenType.Type)
						elementTypeText = ctx.SourceFile.Text()[innerTypeRange.Pos():innerTypeRange.End()]
					}
				}

				newText := fmt.Sprintf("%s<%s>", className, elementTypeText)
				ctx.ReportNodeWithFixes(errorNode, message,
					rule.RuleFixReplace(ctx.SourceFile, errorNode, newText))
			},

			ast.KindTypeReference: func(node *ast.Node) {
				typeRef := node.AsTypeReference()
				if typeRef == nil {
					return
				}

				if !ast.IsIdentifier(typeRef.TypeName) {
					return
				}

				identifier := typeRef.TypeName.AsIdentifier()
				if identifier == nil {
					return
				}
				typeName := identifier.Text

				if typeName != "Array" && typeName != "ReadonlyArray" && typeName != "Readonly" {
					return
				}

				if typeName == "Readonly" {
					if typeRef.TypeArguments == nil || len(typeRef.TypeArguments.Nodes) == 0 {
						return
					}
					if typeRef.TypeArguments.Nodes[0].Kind != ast.KindArrayType {
						return
					}
				}

				isReadonlyWithGenericArrayType := typeName == "Readonly" &&
					typeRef.TypeArguments != nil &&
					len(typeRef.TypeArguments.Nodes) > 0 &&
					typeRef.TypeArguments.Nodes[0].Kind == ast.KindArrayType

				isReadonlyArrayType := typeName == "ReadonlyArray" || isReadonlyWithGenericArrayType

				currentOption := defaultOption
				if isReadonlyArrayType {
					currentOption = readonlyOption
				}

				if currentOption == "generic" {
					return
				}

				readonlyPrefix := ""
				if isReadonlyArrayType {
					readonlyPrefix = "readonly "
				}

				typeParams := typeRef.TypeArguments
				var messageId string
				switch currentOption {
				case "array":
					if isReadonlyWithGenericArrayType {
						messageId = "errorStringArrayReadonly"
					} else {
						messageId = "errorStringArray"
					}
				case "array-simple":

					isSimple := typeParams == nil || len(typeParams.Nodes) == 0 ||
						(len(typeParams.Nodes) == 1 && isSimpleType(typeParams.Nodes[0]))

					if !isSimple {
						return
					}

					if isReadonlyArrayType && typeName != "ReadonlyArray" {
						messageId = "errorStringArraySimpleReadonly"
					} else {
						messageId = "errorStringArraySimple"
					}
				}

				if typeParams == nil || len(typeParams.Nodes) == 0 {

					className := "Array"
					if isReadonlyArrayType {
						className = "ReadonlyArray"
					}

					var message rule.RuleMessage
					switch messageId {
					case "errorStringArray":
						message = buildErrorStringArrayMessage(className, readonlyPrefix, "any")
					case "errorStringArrayReadonly":
						message = buildErrorStringArrayReadonlyMessage(className, readonlyPrefix, "any")
					case "errorStringArraySimple":
						message = buildErrorStringArraySimpleMessage(className, readonlyPrefix, "any")
					case "errorStringArraySimpleReadonly":
						message = buildErrorStringArraySimpleReadonlyMessage(className, readonlyPrefix, "any")
					}

					ctx.ReportNodeWithFixes(node, message,
						rule.RuleFixReplace(ctx.SourceFile, node, readonlyPrefix+"any[]"))
					return
				}

				if len(typeParams.Nodes) != 1 {
					return
				}

				typeParam := typeParams.Nodes[0]

				// Only add parentheses when converting Array<T> -> T[] if T needs them
				// Never add parentheses when converting T[] -> Array<T>
				var typeParens bool
				var parentParens bool

				if currentOption == "array" || currentOption == "array-simple" {

					typeParens = typeNeedsParentheses(typeParam)
					parentParens = readonlyPrefix != "" &&
						node.Parent != nil &&
						node.Parent.Kind == ast.KindArrayType &&
						!isParenthesized(node.Parent.AsArrayTypeNode().ElementType)
				}

				start := ""
				if parentParens {
					start += "("
				}
				start += readonlyPrefix
				if typeParens {
					start += "("
				}

				end := ""
				if typeParens {
					end += ")"
				}
				if !isReadonlyWithGenericArrayType {
					end += "[]"
				}
				if parentParens {
					end += ")"
				}

				typeStr := getMessageType(typeParam)
				className := typeName
				if !isReadonlyArrayType {
					className = "Array"
				}

				var message rule.RuleMessage
				switch messageId {
				case "errorStringArray":
					message = buildErrorStringArrayMessage(className, readonlyPrefix, typeStr)
				case "errorStringArrayReadonly":
					message = buildErrorStringArrayReadonlyMessage(className, readonlyPrefix, typeStr)
				case "errorStringArraySimple":
					message = buildErrorStringArraySimpleMessage(className, readonlyPrefix, typeStr)
				case "errorStringArraySimpleReadonly":
					message = buildErrorStringArraySimpleReadonlyMessage(className, readonlyPrefix, typeStr)
				}

				typeParamRange := utils.TrimNodeTextRange(ctx.SourceFile, typeParam)
				typeParamText := ctx.SourceFile.Text()[typeParamRange.Pos():typeParamRange.End()]

				if (currentOption == "array-simple") && ast.IsParenthesizedTypeNode(typeParam) {

					parenType := typeParam.AsParenthesizedTypeNode()
					if parenType != nil && parenType.Type != nil {
						innerTypeRange := utils.TrimNodeTextRange(ctx.SourceFile, parenType.Type)
						typeParamText = ctx.SourceFile.Text()[innerTypeRange.Pos():innerTypeRange.End()]
					}
				}

				ctx.ReportNodeWithFixes(node, message,
					rule.RuleFixReplace(ctx.SourceFile, node, start+typeParamText+end))
			},
		}
	},
})

Functions

This section is empty.

Types

type ArrayTypeOptions

type ArrayTypeOptions struct {
	Default  string `json:"default"`
	Readonly string `json:"readonly,omitempty"`
}

Jump to

Keyboard shortcuts

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