1
1
import {
2
- ESLintUtils ,
3
- TSESTree ,
4
- ASTUtils ,
5
- } from '@typescript-eslint/experimental-utils' ;
6
- import {
7
- getDocsUrl ,
8
- LIBRARY_MODULES ,
9
- hasTestingLibraryImportModule ,
10
- } from '../utils' ;
11
- import {
2
+ getDeepestIdentifierNode ,
3
+ getPropertyIdentifierNode ,
4
+ getReferenceNode ,
12
5
isObjectPattern ,
13
6
isProperty ,
14
- isCallExpression ,
15
- isLiteral ,
16
- isMemberExpression ,
17
- isImportSpecifier ,
18
- isRenderVariableDeclarator ,
19
7
} from '../node-utils' ;
8
+ import { createTestingLibraryRule } from '../create-testing-library-rule' ;
9
+ import { ASTUtils , TSESTree } from '@typescript-eslint/experimental-utils' ;
20
10
21
11
export const RULE_NAME = 'no-debug' ;
22
12
export type MessageIds = 'noDebug' ;
23
- type Options = [ { renderFunctions ?: string [ ] } ] ;
13
+ type Options = [ ] ;
24
14
25
- export default ESLintUtils . RuleCreator ( getDocsUrl ) < Options , MessageIds > ( {
15
+ export default createTestingLibraryRule < Options , MessageIds > ( {
26
16
name : RULE_NAME ,
27
17
meta : {
28
18
type : 'problem' ,
@@ -46,154 +36,67 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
46
36
} ,
47
37
] ,
48
38
} ,
49
- defaultOptions : [
50
- {
51
- renderFunctions : [ ] ,
52
- } ,
53
- ] ,
54
-
55
- create ( context , [ options ] ) {
56
- let hasDestructuredDebugStatement = false ;
57
- const renderVariableDeclarators : TSESTree . VariableDeclarator [ ] = [ ] ;
58
-
59
- const { renderFunctions } = options ;
39
+ defaultOptions : [ ] ,
60
40
61
- let hasImportedScreen = false ;
62
- let wildcardImportName : string = null ;
41
+ create ( context , [ ] , helpers ) {
42
+ const suspiciousDebugVariableNames : string [ ] = [ ] ;
43
+ const suspiciousReferenceNodes : TSESTree . Identifier [ ] = [ ] ;
63
44
64
45
return {
65
46
VariableDeclarator ( node ) {
66
- if ( isRenderVariableDeclarator ( node , [ 'render' , ...renderFunctions ] ) ) {
67
- if (
68
- isObjectPattern ( node . id ) &&
69
- node . id . properties . some (
70
- ( property ) =>
71
- isProperty ( property ) &&
72
- ASTUtils . isIdentifier ( property . key ) &&
73
- property . key . name === 'debug'
74
- )
75
- ) {
76
- hasDestructuredDebugStatement = true ;
77
- }
78
-
79
- if ( node . id . type === 'Identifier' ) {
80
- renderVariableDeclarators . push ( node ) ;
81
- }
82
- }
83
- } ,
84
- [ `VariableDeclarator > CallExpression > Identifier[name="require"]` ] (
85
- node : TSESTree . Identifier
86
- ) {
87
- const { arguments : args } = node . parent as TSESTree . CallExpression ;
47
+ const initIdentifierNode = getDeepestIdentifierNode ( node . init ) ;
88
48
89
- const literalNodeScreenModuleName = args . find (
90
- ( args ) =>
91
- isLiteral ( args ) &&
92
- typeof args . value === 'string' &&
93
- LIBRARY_MODULES . includes ( args . value )
94
- ) ;
95
-
96
- if ( ! literalNodeScreenModuleName ) {
49
+ if ( ! helpers . isRenderUtil ( initIdentifierNode ) ) {
97
50
return ;
98
51
}
99
52
100
- const declaratorNode = node . parent
101
- . parent as TSESTree . VariableDeclarator ;
102
-
103
- hasImportedScreen =
104
- isObjectPattern ( declaratorNode . id ) &&
105
- declaratorNode . id . properties . some (
106
- ( property ) =>
53
+ // find debug obtained from render and save their name, like:
54
+ // const { debug } = render();
55
+ if ( isObjectPattern ( node . id ) ) {
56
+ for ( const property of node . id . properties ) {
57
+ if (
107
58
isProperty ( property ) &&
108
59
ASTUtils . isIdentifier ( property . key ) &&
109
- property . key . name === 'screen'
110
- ) ;
111
- } ,
112
- // checks if import has shape:
113
- // import { screen } from '@testing-library/dom';
114
- ImportDeclaration ( node : TSESTree . ImportDeclaration ) {
115
- if ( ! hasTestingLibraryImportModule ( node ) ) {
116
- return ;
117
- }
118
-
119
- hasImportedScreen = node . specifiers . some (
120
- ( s ) => isImportSpecifier ( s ) && s . imported . name === 'screen'
121
- ) ;
122
- } ,
123
- // checks if import has shape:
124
- // import * as dtl from '@testing-library/dom';
125
- 'ImportDeclaration ImportNamespaceSpecifier' (
126
- node : TSESTree . ImportNamespaceSpecifier
127
- ) {
128
- const importDeclarationNode = node . parent as TSESTree . ImportDeclaration ;
129
- if ( ! hasTestingLibraryImportModule ( importDeclarationNode ) ) {
130
- return ;
60
+ property . key . name === 'debug'
61
+ ) {
62
+ suspiciousDebugVariableNames . push (
63
+ getDeepestIdentifierNode ( property . value ) . name
64
+ ) ;
65
+ }
66
+ }
131
67
}
132
68
133
- wildcardImportName = node . local && node . local . name ;
134
- } ,
135
- [ `CallExpression > Identifier[name="debug"]` ] ( node : TSESTree . Identifier ) {
136
- if ( hasDestructuredDebugStatement ) {
137
- context . report ( {
138
- node,
139
- messageId : 'noDebug' ,
140
- } ) ;
69
+ // find utils kept from render and save their node, like:
70
+ // const utils = render();
71
+ if ( ASTUtils . isIdentifier ( node . id ) ) {
72
+ suspiciousReferenceNodes . push ( node . id ) ;
141
73
}
142
74
} ,
143
- [ `CallExpression > MemberExpression > Identifier[name="debug"]` ] (
144
- node : TSESTree . Identifier
145
- ) {
146
- const memberExpression = node . parent as TSESTree . MemberExpression ;
147
- const identifier = memberExpression . object as TSESTree . Identifier ;
148
- const memberExpressionName = identifier . name ;
149
- /*
150
- check if `debug` used following the pattern:
151
-
152
- import { screen } from '@testing-library/dom';
153
- ...
154
- screen.debug();
155
- */
156
- const isScreenDebugUsed =
157
- hasImportedScreen && memberExpressionName === 'screen' ;
158
-
159
- /*
160
- check if `debug` used following the pattern:
161
-
162
- import * as dtl from '@testing-library/dom';
163
- ...
164
- dtl.debug();
165
- */
166
- const isNamespaceDebugUsed =
167
- wildcardImportName && memberExpressionName === wildcardImportName ;
75
+ CallExpression ( node ) {
76
+ const callExpressionIdentifier = getDeepestIdentifierNode ( node ) ;
77
+ const referenceNode = getReferenceNode ( node ) ;
78
+ const referenceIdentifier = getPropertyIdentifierNode ( referenceNode ) ;
79
+
80
+ const isDebugUtil = helpers . isDebugUtil ( callExpressionIdentifier ) ;
81
+ const isDeclaredDebugVariable = suspiciousDebugVariableNames . includes (
82
+ callExpressionIdentifier . name
83
+ ) ;
84
+ const isChainedReferenceDebug = suspiciousReferenceNodes . some (
85
+ ( suspiciousReferenceIdentifier ) => {
86
+ return (
87
+ callExpressionIdentifier . name === 'debug' &&
88
+ suspiciousReferenceIdentifier . name === referenceIdentifier . name
89
+ ) ;
90
+ }
91
+ ) ;
168
92
169
- if ( isScreenDebugUsed || isNamespaceDebugUsed ) {
93
+ if ( isDebugUtil || isDeclaredDebugVariable || isChainedReferenceDebug ) {
170
94
context . report ( {
171
- node,
95
+ node : callExpressionIdentifier ,
172
96
messageId : 'noDebug' ,
173
97
} ) ;
174
98
}
175
99
} ,
176
- 'Program:exit' ( ) {
177
- renderVariableDeclarators . forEach ( ( renderVar ) => {
178
- const renderVarReferences = context
179
- . getDeclaredVariables ( renderVar ) [ 0 ]
180
- . references . slice ( 1 ) ;
181
- renderVarReferences . forEach ( ( ref ) => {
182
- const parent = ref . identifier . parent ;
183
- if (
184
- isMemberExpression ( parent ) &&
185
- ASTUtils . isIdentifier ( parent . property ) &&
186
- parent . property . name === 'debug' &&
187
- isCallExpression ( parent . parent )
188
- ) {
189
- context . report ( {
190
- node : parent . property ,
191
- messageId : 'noDebug' ,
192
- } ) ;
193
- }
194
- } ) ;
195
- } ) ;
196
- } ,
197
100
} ;
198
101
} ,
199
102
} ) ;
0 commit comments