1
1
import type { Rule } from 'eslint' ;
2
+ import type { Except } from 'type-fest' ;
2
3
import { createMochaVisitors , type VisitorContext } from '../ast/mocha-visitors.js' ;
4
+ import {
5
+ type AnyFunction ,
6
+ isBlockStatement ,
7
+ isFunction ,
8
+ isMemberExpression ,
9
+ isProgram ,
10
+ type Program
11
+ } from '../ast/node-types.js' ;
3
12
import { getLastOrThrow } from '../list.js' ;
4
13
5
14
const minimumAmountOfLinesBetweenNeeded = 2 ;
6
15
7
- function isFirstStatementInScope ( node : Readonly < Rule . Node > ) : boolean {
8
- // @ts -expect-error -- ok in this case
9
- return node . parent . parent . body [ 0 ] === node . parent ; // eslint-disable-line @typescript-eslint/no-unsafe-member-access -- ok in this case
16
+ function containsNode ( nodeA : Except < Rule . Node , 'parent' > , nodeB : Except < Rule . Node , 'parent' > ) : boolean {
17
+ const { range : rangeA } = nodeA ;
18
+ const { range : rangeB } = nodeB ;
19
+ if ( rangeA === undefined || rangeB === undefined ) {
20
+ return false ;
21
+ }
22
+
23
+ return rangeB [ 1 ] <= rangeA [ 1 ] && rangeB [ 0 ] >= rangeA [ 0 ] ;
24
+ }
25
+
26
+ function isFirstStatementInScope ( scopeNode : Layer [ 'scopeNode' ] , node : Rule . Node ) : boolean {
27
+ if ( isBlockStatement ( scopeNode ) || isProgram ( scopeNode ) ) {
28
+ const [ firstNode ] = scopeNode . body ;
29
+ if ( firstNode !== undefined ) {
30
+ return containsNode ( firstNode , node ) ;
31
+ }
32
+ }
33
+
34
+ return containsNode ( scopeNode , node ) ;
10
35
}
11
36
12
37
type Layer = {
13
38
entities : VisitorContext [ ] ;
39
+ scopeNode : AnyFunction [ 'body' ] | Program ;
14
40
} ;
15
41
42
+ function getParentWhileMemberExpression ( node : Rule . Node ) : Rule . Node {
43
+ if ( isMemberExpression ( node . parent ) ) {
44
+ return getParentWhileMemberExpression ( node . parent ) ;
45
+ }
46
+ return node ;
47
+ }
48
+
16
49
export const consistentSpacingBetweenBlocksRule : Readonly < Rule . RuleModule > = {
17
50
meta : {
18
51
type : 'suggestion' ,
@@ -26,7 +59,7 @@ export const consistentSpacingBetweenBlocksRule: Readonly<Rule.RuleModule> = {
26
59
} ,
27
60
28
61
create ( context ) {
29
- const layers : [ Layer , ... Layer [ ] ] = [ { entities : [ ] } ] ;
62
+ const layers : Layer [ ] = [ ] ;
30
63
const { sourceCode } = context ;
31
64
32
65
function addEntityToCurrentLayer ( visitorContext : Readonly < VisitorContext > ) : void {
@@ -39,15 +72,15 @@ export const consistentSpacingBetweenBlocksRule: Readonly<Rule.RuleModule> = {
39
72
const currentLayer = getLastOrThrow ( layers ) ;
40
73
41
74
for ( const entity of currentLayer . entities ) {
42
- const { node } = entity ;
75
+ const node = getParentWhileMemberExpression ( entity . node ) ;
43
76
const beforeToken = sourceCode . getTokenBefore ( node ) ;
44
77
45
- if ( ! isFirstStatementInScope ( node ) && beforeToken !== null ) {
78
+ if ( ! isFirstStatementInScope ( currentLayer . scopeNode , node ) && beforeToken !== null ) {
46
79
const linesBetween = ( node . loc ?. start . line ?? 0 ) - ( beforeToken . loc . end . line ) ;
47
80
48
81
if ( linesBetween < minimumAmountOfLinesBetweenNeeded ) {
49
82
context . report ( {
50
- node,
83
+ node : entity . node ,
51
84
message : 'Expected line break before this statement.' ,
52
85
fix ( fixer ) {
53
86
return fixer . insertTextAfter (
@@ -64,14 +97,24 @@ export const consistentSpacingBetweenBlocksRule: Readonly<Rule.RuleModule> = {
64
97
return createMochaVisitors ( context , {
65
98
suite ( visitorContext ) {
66
99
addEntityToCurrentLayer ( visitorContext ) ;
67
- layers . push ( { entities : [ ] } ) ;
68
100
} ,
69
101
70
- 'suite:exit' ( ) {
102
+ suiteCallback ( visitorContext ) {
103
+ const { node } = visitorContext ;
104
+ if ( isFunction ( node ) ) {
105
+ layers . push ( { entities : [ ] , scopeNode : node . body } ) ;
106
+ }
107
+ } ,
108
+
109
+ 'suiteCallback:exit' ( ) {
71
110
checkCurrentLayer ( ) ;
72
111
layers . pop ( ) ;
73
112
} ,
74
113
114
+ Program ( node ) {
115
+ layers . push ( { entities : [ ] , scopeNode : node } ) ;
116
+ } ,
117
+
75
118
'Program:exit' ( ) {
76
119
checkCurrentLayer ( ) ;
77
120
} ,
0 commit comments