Skip to content

Commit 1ab9e64

Browse files
committed
refactor: boil down complexity
1 parent 93c5a12 commit 1ab9e64

File tree

1 file changed

+34
-121
lines changed

1 file changed

+34
-121
lines changed

src/rules/consistent-spacing-between-blocks.ts

Lines changed: 34 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Rule } from 'eslint'
2-
import type { CallExpression } from 'estree'
2+
import type { BlockStatement, CallExpression, Program } from 'estree'
33
import { getParent } from '../utils/ast.js'
44
import { createRule } from '../utils/createRule.js'
55
import { parseFnCall } from '../utils/parseFnCall.js'
@@ -8,146 +8,59 @@ export default createRule({
88
create(context) {
99
const { sourceCode } = context
1010

11-
function isPrecededByTokens(node: Rule.Node, testTokens: string[]) {
12-
const tokenBefore = sourceCode.getTokenBefore(node)
13-
return tokenBefore && testTokens.includes(tokenBefore.value)
11+
function isBlockOrProgram(
12+
node: Rule.Node,
13+
): node is
14+
| (BlockStatement & Rule.NodeParentExtension)
15+
| (Program & Rule.NodeParentExtension) {
16+
return node.type === 'BlockStatement' || node.type === 'Program'
1417
}
1518

16-
function isFirstNode(node: Rule.Node) {
19+
function getStatementNode(node: Rule.Node): Rule.Node {
1720
const parent = getParent(node)
18-
if (!parent) return true
19-
20-
const parentType = parent.type
21-
if (
22-
parentType === 'ExpressionStatement' ||
23-
parentType === 'VariableDeclaration'
24-
) {
25-
const realParent = getParent(parent)
26-
if ('body' in realParent && realParent.body) {
27-
const { body } = realParent
28-
return Array.isArray(body) ? body[0] === node : body === parent
29-
}
30-
return false
31-
}
32-
33-
if (parentType === 'IfStatement') {
34-
return isPrecededByTokens(node, ['else', ')'])
35-
}
36-
37-
if (parentType === 'DoWhileStatement') {
38-
return isPrecededByTokens(node, ['do'])
39-
}
40-
41-
if (parentType === 'SwitchCase') {
42-
return isPrecededByTokens(node, [':'])
43-
}
44-
45-
if ('body' in parent && parent.body) {
46-
const { body } = parent
47-
return Array.isArray(body) ? body[0] === node : body === node
48-
}
49-
50-
return isPrecededByTokens(node, [')'])
51-
}
52-
53-
function calcCommentLines(node: Rule.Node, lineNumTokenBefore: number) {
54-
const comments = sourceCode.getCommentsBefore(node)
55-
let numLinesComments = 0
56-
57-
if (!comments.length) {
58-
return numLinesComments
59-
}
60-
61-
comments.forEach((comment) => {
62-
numLinesComments++
63-
64-
if (comment.type === 'Block') {
65-
numLinesComments += comment.loc!.end.line - comment.loc!.start.line
66-
}
67-
68-
// avoid counting lines with inline comments twice
69-
if (comment.loc!.start.line === lineNumTokenBefore) {
70-
numLinesComments--
71-
}
72-
73-
if (comment.loc!.end.line === node.loc!.start.line) {
74-
numLinesComments--
75-
}
76-
})
77-
78-
return numLinesComments
21+
if (!parent) return node
22+
if (isBlockOrProgram(parent)) return node
23+
return getStatementNode(parent)
7924
}
8025

81-
function hasNewlineBefore(node: Rule.Node) {
82-
const tokenBefore = sourceCode.getTokenBefore(node)
83-
const lineNumTokenBefore = !tokenBefore ? 0 : tokenBefore.loc.end.line
84-
const lineNumNode = node.loc!.start.line
85-
const commentLines = calcCommentLines(node, lineNumTokenBefore)
86-
87-
return lineNumNode - lineNumTokenBefore - commentLines > 1
26+
function isFirstStatementInBlock(node: Rule.Node): boolean {
27+
const parent = getParent(node)
28+
if (!parent) return true
29+
if (isBlockOrProgram(parent)) return parent.body[0] === node
30+
return false
8831
}
8932

90-
function getRealNodeToCheck(
91-
node: CallExpression & Rule.NodeParentExtension,
92-
) {
93-
const parent = getParent(node)
94-
if (!parent) return node
33+
function checkSpacing(node: CallExpression & Rule.NodeParentExtension) {
34+
const statementNode = getStatementNode(node)
9535

96-
if (parent.type === 'ExpressionStatement') {
97-
return parent
98-
}
99-
if (parent.type === 'AwaitExpression') {
100-
const awaitParent = getParent(parent)
101-
return awaitParent.type === 'ExpressionStatement'
102-
? awaitParent
103-
: getParent(awaitParent)
104-
}
105-
if (
106-
parent.type === 'VariableDeclarator' ||
107-
parent.type === 'AssignmentExpression'
108-
) {
109-
return getParent(parent)
110-
}
36+
if (isFirstStatementInBlock(statementNode)) return
11137

112-
return node
113-
}
38+
const comments = sourceCode.getCommentsBefore(statementNode)
39+
const nodeToCheck = comments.length > 0 ? comments[0] : statementNode
40+
const lineNumber = nodeToCheck.loc!.start.line
11441

115-
function checkSpacing(node: CallExpression & Rule.NodeParentExtension) {
116-
const nodeToCheck = getRealNodeToCheck(node)
42+
if (lineNumber === 1) return
11743

118-
if (isFirstNode(nodeToCheck)) return
119-
if (hasNewlineBefore(nodeToCheck)) return
44+
const lines = sourceCode.lines
45+
const previousLine = lines[lineNumber - 2]
12046

121-
const leadingComments = sourceCode.getCommentsBefore(nodeToCheck)
122-
const firstComment = leadingComments[0]
123-
const reportLoc = firstComment?.loc ?? nodeToCheck.loc
47+
if (previousLine.trim() === '') return
12448

12549
context.report({
12650
data: {
127-
source: sourceCode.getText(nodeToCheck).split('\n')[0],
51+
source: sourceCode.getText(statementNode).split('\n')[0],
12852
},
12953
fix(fixer) {
130-
const tokenBefore = sourceCode.getTokenBefore(nodeToCheck)
131-
if (!tokenBefore) return null
132-
133-
const newlines =
134-
nodeToCheck.loc?.start.line === tokenBefore.loc.end.line
135-
? '\n\n'
136-
: '\n'
137-
const targetNode = firstComment ?? nodeToCheck
138-
const nodeStart = targetNode.range?.[0] ?? 0
139-
const textBeforeNode = sourceCode.text.substring(0, nodeStart)
140-
const lastNewlineIndex = textBeforeNode.lastIndexOf('\n')
141-
const insertPosition = lastNewlineIndex + 1
54+
const nodeStart = nodeToCheck.range![0]
55+
const textBefore = sourceCode.text.substring(0, nodeStart)
56+
const lastNewlineIndex = textBefore.lastIndexOf('\n')
57+
const lineStart = lastNewlineIndex + 1
14258

143-
return fixer.insertTextBeforeRange(
144-
[insertPosition, nodeStart],
145-
newlines,
146-
)
59+
return fixer.insertTextBeforeRange([lineStart, nodeStart], '\n')
14760
},
148-
loc: reportLoc!,
61+
loc: nodeToCheck.loc!,
14962
messageId: 'missingWhitespace',
150-
node: nodeToCheck,
63+
node: statementNode,
15164
})
15265
}
15366

0 commit comments

Comments
 (0)