Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ObjectShorthandRule = rule.Rule{ Name: "object-shorthand", Run: func(ctx rule.RuleContext, optionsAny any) rule.RuleListeners { opts := parseOptions(optionsAny) sourceText := ctx.SourceFile.Text() applyToMethods := opts.apply == modeMethods || opts.apply == modeAlways applyToProps := opts.apply == modeProperties || opts.apply == modeAlways applyNever := opts.apply == modeNever applyConsistent := opts.apply == modeConsistent applyConsistentAsNeeded := opts.apply == modeConsistentAsNeeded lexicalScopeStack := []map[*ast.Node]bool{{}} arrowsWithLexicalIdentifiers := map[*ast.Node]bool{} enterScope := func() { lexicalScopeStack = append(lexicalScopeStack, map[*ast.Node]bool{}) } exitScope := func() { if len(lexicalScopeStack) > 0 { lexicalScopeStack = lexicalScopeStack[:len(lexicalScopeStack)-1] } } markCurrentLexical := func() { if len(lexicalScopeStack) == 0 { return } for arrow := range lexicalScopeStack[len(lexicalScopeStack)-1] { arrowsWithLexicalIdentifiers[arrow] = true } } msgExpectedPropertyShorthand := rule.RuleMessage{ Id: "expectedPropertyShorthand", Description: "Expected property shorthand.", } msgExpectedMethodShorthand := rule.RuleMessage{ Id: "expectedMethodShorthand", Description: "Expected method shorthand.", } msgExpectedPropertyLongform := rule.RuleMessage{ Id: "expectedPropertyLongform", Description: "Expected longform property syntax.", } msgExpectedMethodLongform := rule.RuleMessage{ Id: "expectedMethodLongform", Description: "Expected longform method syntax.", } msgExpectedLiteralMethodLongform := rule.RuleMessage{ Id: "expectedLiteralMethodLongform", Description: "Expected longform method syntax for string literal keys.", } msgExpectedAllPropertiesShorthanded := rule.RuleMessage{ Id: "expectedAllPropertiesShorthanded", Description: "Expected shorthand for all properties.", } msgUnexpectedMix := rule.RuleMessage{ Id: "unexpectedMix", Description: "Unexpected mix of shorthand and non-shorthand properties.", } keyText := func(nameNode *ast.Node) string { if nameNode == nil { return "" } r := utils.TrimNodeTextRange(ctx.SourceFile, nameNode) return sourceText[r.Pos():r.End()] } fixShorthandProperty := func(node *ast.Node, valueName string) []rule.RuleFix { if hasCommentsInsideText(sourceText, node.Pos(), node.End()) { return nil } return []rule.RuleFix{rule.RuleFixReplace(ctx.SourceFile, node, valueName)} } fixPropertyToLongform := func(node *ast.Node) []rule.RuleFix { sp := node.AsShorthandPropertyAssignment() if sp == nil { return nil } name := sp.Name() if name == nil || name.Kind != ast.KindIdentifier { return nil } ident := name.AsIdentifier().Text return []rule.RuleFix{rule.RuleFixInsertAfter(name, ": "+ident)} } fixMethodToLongform := func(node *ast.Node) []rule.RuleFix { method := node.AsMethodDeclaration() if method == nil || method.Body == nil { return nil } isAsync := ast.HasSyntacticModifier(node, ast.ModifierFlagsAsync) isGenerator := method.AsteriskToken != nil nameNode := method.Name() if nameNode == nil { return nil } nodeRange := utils.TrimNodeTextRange(ctx.SourceFile, node) nameRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode) keyStr := keyText(nameNode) header := "function" if isAsync { header = "async function" } if isGenerator { header += "*" } replaceRange := core.NewTextRange(nodeRange.Pos(), nameRange.End()) return []rule.RuleFix{rule.RuleFixReplaceRange(replaceRange, keyStr+": "+header)} } fixFunctionToMethod := func(node *ast.Node) []rule.RuleFix { pa := node.AsPropertyAssignment() fn := propertyValue(pa) if fn == nil || fn.Kind != ast.KindFunctionExpression { return nil } nameNode := pa.Name() if nameNode == nil { return nil } keyRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode) fnRange := utils.TrimNodeTextRange(ctx.SourceFile, fn) if utils.HasCommentsInRange(ctx.SourceFile, core.NewTextRange(keyRange.End(), fnRange.Pos())) { return nil } fe := fn.AsFunctionExpression() if fe == nil || fe.Body == nil { return nil } isAsync := ast.HasSyntacticModifier(fn, ast.ModifierFlagsAsync) isGenerator := fe.AsteriskToken != nil prefix := "" if isAsync { prefix += "async " } if isGenerator { prefix += "*" } // The tail we want to keep is everything after `function` (and // `*` when generator). For generators, AsteriskToken gives us a // precise end position; otherwise we scan for the `function` // keyword past any leading modifiers (e.g. `async`). var headerEnd int if isGenerator { headerEnd = fe.AsteriskToken.End() } else { kwEnd, ok := positionAfterFunctionKeyword(ctx.SourceFile, fn) if !ok { return nil } headerEnd = kwEnd } nodeRange := utils.TrimNodeTextRange(ctx.SourceFile, node) keyStr := keyText(nameNode) tail := sourceText[headerEnd:fnRange.End()] return []rule.RuleFix{rule.RuleFixReplaceRange( core.NewTextRange(nodeRange.Pos(), nodeRange.End()), prefix+keyStr+tail, )} } fixArrowToMethod := func(node *ast.Node) []rule.RuleFix { pa := node.AsPropertyAssignment() fn := propertyValue(pa) if fn == nil || fn.Kind != ast.KindArrowFunction { return nil } arrow := fn.AsArrowFunction() if arrow == nil || arrow.Body == nil { return nil } if arrow.Body.Kind != ast.KindBlock { return nil } nameNode := pa.Name() if nameNode == nil { return nil } keyRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode) fnRange := utils.TrimNodeTextRange(ctx.SourceFile, fn) if utils.HasCommentsInRange(ctx.SourceFile, core.NewTextRange(keyRange.End(), fnRange.Pos())) { return nil } isAsync := ast.HasSyntacticModifier(fn, ast.ModifierFlagsAsync) if arrow.EqualsGreaterThanToken == nil { return nil } paramsStart := fnRange.Pos() if mods := arrow.Modifiers(); isAsync && mods != nil && len(mods.Nodes) > 0 { paramsStart = mods.End() } arrowTokenPos := arrow.EqualsGreaterThanToken.Pos() arrowTokenEnd := arrow.EqualsGreaterThanToken.End() paramsText := strings.TrimSpace(sourceText[paramsStart:arrowTokenPos]) bodyText := strings.TrimLeft(sourceText[arrowTokenEnd:fnRange.End()], " \t") if len(paramsText) == 0 || paramsText[0] != '(' { paramsText = "(" + paramsText + ")" } prefix := "" if isAsync { prefix = "async " } nodeRange := utils.TrimNodeTextRange(ctx.SourceFile, node) keyStr := keyText(nameNode) replacement := prefix + keyStr + paramsText + " " + bodyText return []rule.RuleFix{rule.RuleFixReplaceRange( core.NewTextRange(nodeRange.Pos(), nodeRange.End()), replacement, )} } reportMix := func(obj *ast.Node) { ctx.ReportNode(obj, msgUnexpectedMix) } reportAllShorthand := func(obj *ast.Node) { ctx.ReportNode(obj, msgExpectedAllPropertiesShorthanded) } checkConsistency := func(obj *ast.Node, checkRedundancy bool) { ol := obj.AsObjectLiteralExpression() if ol == nil || ol.Properties == nil { return } var considered []*ast.Node for _, p := range ol.Properties.Nodes { if canHaveShorthand(p) { considered = append(considered, p) } } if len(considered) == 0 { return } shorthandCount := 0 for _, p := range considered { if isShorthandKind(classify(p)) { shorthandCount++ } } if shorthandCount == len(considered) { return } if shorthandCount > 0 { reportMix(obj) return } if !checkRedundancy { return } allRedundant := true for _, p := range considered { if !isRedundantLongform(p) { allRedundant = false break } } if allRedundant { reportAllShorthand(obj) } } handleProperty := func(node *ast.Node) { if node.Parent == nil || node.Parent.Kind != ast.KindObjectLiteralExpression { return } kind := classify(node) if kind == propKindOther { return } isConcise := kind == propKindShorthandProp || kind == propKindShorthandMethod if node.Kind == ast.KindPropertyAssignment { pa := node.AsPropertyAssignment() if pa != nil && pa.Name() != nil && pa.Name().Kind == ast.KindComputedPropertyName { if pa.Initializer != nil && pa.Initializer.Kind != ast.KindFunctionExpression && pa.Initializer.Kind != ast.KindArrowFunction { return } } } if isConcise { if kind == propKindShorthandMethod { method := node.AsMethodDeclaration() var keyNode *ast.Node if method != nil { keyNode = method.Name() } if applyNever || (opts.avoidQuotes && isStringLiteralKey(keyNode)) { msg := msgExpectedMethodLongform if !applyNever && opts.avoidQuotes { msg = msgExpectedLiteralMethodLongform } if fixes := fixMethodToLongform(node); fixes != nil { ctx.ReportNodeWithFixes(node, msg, fixes...) } else { ctx.ReportNode(node, msg) } } } else if applyNever { if fixes := fixPropertyToLongform(node); fixes != nil { ctx.ReportNodeWithFixes(node, msgExpectedPropertyLongform, fixes...) } else { ctx.ReportNode(node, msgExpectedPropertyLongform) } } return } pa := node.AsPropertyAssignment() value := propertyValue(pa) if value == nil { return } valueKind := value.Kind if applyToMethods && (valueKind == ast.KindFunctionExpression || valueKind == ast.KindArrowFunction) { if valueKind == ast.KindFunctionExpression { if value.AsFunctionExpression().Name() != nil { return } } nameNode := pa.Name() if opts.ignoreConstructors && nameNode != nil && nameNode.Kind == ast.KindIdentifier { if utils.IsConstructorName(nameNode.AsIdentifier().Text) { return } } if shouldIgnoreMethodName(&opts, nameNode) { return } if opts.avoidQuotes && isStringLiteralKey(nameNode) { return } if valueKind == ast.KindFunctionExpression { if fixes := fixFunctionToMethod(node); fixes != nil { ctx.ReportNodeWithFixes(node, msgExpectedMethodShorthand, fixes...) } else { ctx.ReportNode(node, msgExpectedMethodShorthand) } return } arrow := value.AsArrowFunction() if arrow == nil || arrow.Body == nil || arrow.Body.Kind != ast.KindBlock { return } if !opts.avoidExplicitReturnArrows { return } if arrowsWithLexicalIdentifiers[value] { return } if fixes := fixArrowToMethod(node); fixes != nil { ctx.ReportNodeWithFixes(node, msgExpectedMethodShorthand, fixes...) } else { ctx.ReportNode(node, msgExpectedMethodShorthand) } return } if !applyToProps { return } if valueKind != ast.KindIdentifier { return } valueIdent := value.AsIdentifier() if valueIdent == nil { return } nameNode := pa.Name() if nameNode == nil { return } switch nameNode.Kind { case ast.KindIdentifier: if nameNode.AsIdentifier().Text != valueIdent.Text { return } if hasJSDocTypeAnnotationInside(sourceText, node, false) { return } if fixes := fixShorthandProperty(node, valueIdent.Text); fixes != nil { ctx.ReportNodeWithFixes(node, msgExpectedPropertyShorthand, fixes...) } else { ctx.ReportNode(node, msgExpectedPropertyShorthand) } case ast.KindStringLiteral: if nameNode.AsStringLiteral().Text != valueIdent.Text { return } if opts.avoidQuotes { return } if hasJSDocTypeAnnotationInside(sourceText, node, true) { return } if fixes := fixShorthandProperty(node, valueIdent.Text); fixes != nil { ctx.ReportNodeWithFixes(node, msgExpectedPropertyShorthand, fixes...) } else { ctx.ReportNode(node, msgExpectedPropertyShorthand) } } } listeners := rule.RuleListeners{ ast.KindFunctionDeclaration: func(n *ast.Node) { enterScope() }, rule.ListenerOnExit(ast.KindFunctionDeclaration): func(n *ast.Node) { exitScope() }, ast.KindFunctionExpression: func(n *ast.Node) { enterScope() }, rule.ListenerOnExit(ast.KindFunctionExpression): func(n *ast.Node) { exitScope() }, ast.KindMethodDeclaration: func(n *ast.Node) { enterScope() handleProperty(n) }, rule.ListenerOnExit(ast.KindMethodDeclaration): func(n *ast.Node) { exitScope() }, ast.KindGetAccessor: func(n *ast.Node) { enterScope() }, rule.ListenerOnExit(ast.KindGetAccessor): func(n *ast.Node) { exitScope() }, ast.KindSetAccessor: func(n *ast.Node) { enterScope() }, rule.ListenerOnExit(ast.KindSetAccessor): func(n *ast.Node) { exitScope() }, ast.KindConstructor: func(n *ast.Node) { enterScope() }, rule.ListenerOnExit(ast.KindConstructor): func(n *ast.Node) { exitScope() }, ast.KindArrowFunction: func(n *ast.Node) { if len(lexicalScopeStack) > 0 { lexicalScopeStack[len(lexicalScopeStack)-1][n] = true } }, rule.ListenerOnExit(ast.KindArrowFunction): func(n *ast.Node) { if len(lexicalScopeStack) > 0 { delete(lexicalScopeStack[len(lexicalScopeStack)-1], n) } }, ast.KindThisKeyword: func(n *ast.Node) { markCurrentLexical() }, ast.KindSuperKeyword: func(n *ast.Node) { markCurrentLexical() }, ast.KindMetaProperty: func(n *ast.Node) { mp := n.AsMetaProperty() if mp != nil && mp.KeywordToken == ast.KindNewKeyword { markCurrentLexical() } }, ast.KindIdentifier: func(n *ast.Node) { if !isArgumentsIdentifier(n) { return } if len(lexicalScopeStack) <= 1 { return } if isArgumentsShadowedInBlockScope(n) { return } markCurrentLexical() }, rule.ListenerOnExit(ast.KindPropertyAssignment): func(n *ast.Node) { handleProperty(n) }, ast.KindShorthandPropertyAssignment: func(n *ast.Node) { if n.Parent != nil && n.Parent.Kind != ast.KindObjectLiteralExpression { return } handleProperty(n) }, ast.KindObjectLiteralExpression: func(n *ast.Node) { if applyConsistent { checkConsistency(n, false) } else if applyConsistentAsNeeded { checkConsistency(n, true) } }, } return listeners }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.