eslint-plugin-jest-formatting 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +2 -2
- package/.eslintrc +15 -4
- package/CHANGELOG.md +28 -6
- package/README.md +21 -13
- package/docs/rules/padding-around-all.md +13 -0
- package/lib/ast-utils.js +41 -0
- package/lib/index.js +159 -76
- package/lib/rules/padding.js +131 -158
- package/package.json +20 -19
- package/src/ast-utils.ts +80 -0
- package/src/index.ts +166 -81
- package/src/rules/padding.ts +263 -252
- package/tests/catfood.js +41 -0
- package/tests/dogfood.spec.js +2 -0
- package/tests/lib/ast-utils.spec.ts +94 -0
- package/tests/lib/rules/padding-around-after-all-blocks.spec.js +3 -8
- package/tests/lib/rules/padding-around-after-each-blocks.spec.js +3 -8
- package/tests/lib/rules/padding-around-before-all-blocks.spec.js +3 -8
- package/tests/lib/rules/padding-around-before-each-blocks.spec.js +3 -8
- package/tests/lib/rules/padding-around-describe-blocks.spec.js +7 -12
- package/tests/lib/rules/padding-around-expect-groups.spec.js +3 -8
- package/tests/lib/rules/padding-around-test-blocks.spec.js +15 -12
- package/tsconfig.json +1 -1
- package/lib/utils.js +0 -15
- package/src/utils.ts +0 -40
- package/tests/lib/rules/padding-before-after-all-blocks.spec.js +0 -104
- package/tests/lib/rules/padding-before-after-each-blocks.spec.js +0 -104
- package/tests/lib/rules/padding-before-before-all-blocks.spec.js +0 -104
- package/tests/lib/rules/padding-before-before-each-blocks.spec.js +0 -104
- package/tests/lib/rules/padding-before-describe-blocks.spec.js +0 -150
- package/tests/lib/rules/padding-before-expect-statements.spec.js +0 -144
- package/tests/lib/rules/padding-before-test-blocks.spec.js +0 -159
- package/yarn.lock +0 -4232
package/src/rules/padding.ts
CHANGED
|
@@ -1,115 +1,144 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Based on eslint/padding-line-between-statements by Toru Nagashima
|
|
4
|
-
* See: https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js
|
|
2
|
+
* Require/fix newlines around jest functions
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Based on eslint/padding-line-between-statements by Toru Nagashima
|
|
5
|
+
* See: https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js
|
|
6
|
+
*
|
|
7
|
+
* Some helpers borrowed from eslint ast-utils by Gyandeep Singh
|
|
8
|
+
* See: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
|
|
9
9
|
*/
|
|
10
|
+
|
|
10
11
|
import { AST, Rule, SourceCode } from 'eslint';
|
|
12
|
+
// This is because we are using @types/estree that are brought in with eslint
|
|
13
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
11
14
|
import { Node } from 'estree';
|
|
15
|
+
import * as astUtils from '../ast-utils';
|
|
16
|
+
|
|
17
|
+
// Statement types we'll respond to
|
|
18
|
+
export const enum StatementType {
|
|
19
|
+
Any,
|
|
20
|
+
AfterAllToken,
|
|
21
|
+
AfterEachToken,
|
|
22
|
+
BeforeAllToken,
|
|
23
|
+
BeforeEachToken,
|
|
24
|
+
DescribeToken,
|
|
25
|
+
ExpectToken,
|
|
26
|
+
FdescribeToken,
|
|
27
|
+
FitToken,
|
|
28
|
+
ItToken,
|
|
29
|
+
TestToken,
|
|
30
|
+
XdescribeToken,
|
|
31
|
+
XitToken,
|
|
32
|
+
XtestToken,
|
|
33
|
+
}
|
|
12
34
|
|
|
13
|
-
type
|
|
14
|
-
[test: string]: (node: Node, sourceCode: SourceCode) => boolean;
|
|
15
|
-
};
|
|
35
|
+
type StatementTypes = StatementType | StatementType[];
|
|
16
36
|
|
|
17
|
-
type
|
|
18
|
-
[verify: string]: (
|
|
19
|
-
context: Rule.RuleContext,
|
|
20
|
-
prevNode: Node,
|
|
21
|
-
nextNode: Node,
|
|
22
|
-
paddingLines: AST.Token[][],
|
|
23
|
-
) => void;
|
|
24
|
-
};
|
|
37
|
+
type StatementTester = (node: Node, sourceCode: SourceCode) => boolean;
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
]);
|
|
39
|
+
// Padding type to apply between statements
|
|
40
|
+
export const enum PaddingType {
|
|
41
|
+
Any,
|
|
42
|
+
Always,
|
|
43
|
+
}
|
|
32
44
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
type PaddingTester = (
|
|
46
|
+
prevNode: Node,
|
|
47
|
+
nextNode: Node,
|
|
48
|
+
paddingContext: PaddingContext,
|
|
49
|
+
) => void;
|
|
50
|
+
|
|
51
|
+
// A configuration object for padding type and the two statement types
|
|
52
|
+
export interface Config {
|
|
53
|
+
paddingType: PaddingType;
|
|
54
|
+
prevStatementType: StatementTypes;
|
|
55
|
+
nextStatementType: StatementTypes;
|
|
56
|
+
}
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
// Tracks position in scope and prevNode. Used to compare current and prev node
|
|
59
|
+
// and then to walk back up to the parent scope or down into the next one.
|
|
60
|
+
// And so on...
|
|
61
|
+
interface Scope {
|
|
62
|
+
upper: Scope | null;
|
|
63
|
+
prevNode: Node | null;
|
|
64
|
+
}
|
|
40
65
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
interface ScopeInfo {
|
|
67
|
+
prevNode: Node | null;
|
|
68
|
+
enter: () => void;
|
|
69
|
+
exit: () => void;
|
|
70
|
+
}
|
|
44
71
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
test: (node: Node, sourceCode: SourceCode): boolean => {
|
|
51
|
-
const token = sourceCode.getFirstToken(node);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
node.type === 'ExpressionStatement' &&
|
|
55
|
-
token.type === 'Identifier' &&
|
|
56
|
-
token.value === name
|
|
57
|
-
);
|
|
58
|
-
},
|
|
59
|
-
};
|
|
72
|
+
interface PaddingContext {
|
|
73
|
+
ruleContext: Rule.RuleContext;
|
|
74
|
+
sourceCode: SourceCode;
|
|
75
|
+
scopeInfo: ScopeInfo;
|
|
76
|
+
configs: Config[];
|
|
60
77
|
}
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* For example:
|
|
67
|
-
*
|
|
68
|
-
* foo()
|
|
69
|
-
* ;[1, 2, 3].forEach(bar)
|
|
70
|
-
*/
|
|
71
|
-
function getActualLastToken(sourceCode: SourceCode, node: Node): AST.Token {
|
|
72
|
-
const semiToken = sourceCode.getLastToken(node);
|
|
73
|
-
const prevToken = sourceCode.getTokenBefore(semiToken);
|
|
74
|
-
const nextToken = sourceCode.getTokenAfter(semiToken);
|
|
75
|
-
const isSemicolonLessStyle = Boolean(
|
|
76
|
-
prevToken &&
|
|
77
|
-
nextToken &&
|
|
78
|
-
prevToken.range[0] >= node.range[0] &&
|
|
79
|
-
isSemicolonToken(semiToken) &&
|
|
80
|
-
semiToken.loc.start.line !== prevToken.loc.end.line &&
|
|
81
|
-
semiToken.loc.end.line === nextToken.loc.start.line,
|
|
82
|
-
);
|
|
79
|
+
// Creates a StatementTester to test an ExpressionStatement's first token name
|
|
80
|
+
const createTokenTester = (tokenName: string): StatementTester => {
|
|
81
|
+
return (node: Node, sourceCode: SourceCode): boolean => {
|
|
82
|
+
const token = sourceCode.getFirstToken(node);
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
return (
|
|
85
|
+
node.type === 'ExpressionStatement' &&
|
|
86
|
+
token.type === 'Identifier' &&
|
|
87
|
+
token.value === tokenName
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// A mapping of StatementType to StatementTester for... testing statements
|
|
93
|
+
const statementTesters: { [T in StatementType]: StatementTester } = {
|
|
94
|
+
[StatementType.Any]: () => true,
|
|
95
|
+
[StatementType.AfterAllToken]: createTokenTester('afterAll'),
|
|
96
|
+
[StatementType.AfterEachToken]: createTokenTester('afterEach'),
|
|
97
|
+
[StatementType.BeforeAllToken]: createTokenTester('beforeAll'),
|
|
98
|
+
[StatementType.BeforeEachToken]: createTokenTester('beforeEach'),
|
|
99
|
+
[StatementType.DescribeToken]: createTokenTester('describe'),
|
|
100
|
+
[StatementType.ExpectToken]: createTokenTester('expect'),
|
|
101
|
+
[StatementType.FdescribeToken]: createTokenTester('fdescribe'),
|
|
102
|
+
[StatementType.FitToken]: createTokenTester('fit'),
|
|
103
|
+
[StatementType.ItToken]: createTokenTester('it'),
|
|
104
|
+
[StatementType.TestToken]: createTokenTester('test'),
|
|
105
|
+
[StatementType.XdescribeToken]: createTokenTester('xdescribe'),
|
|
106
|
+
[StatementType.XitToken]: createTokenTester('xit'),
|
|
107
|
+
[StatementType.XtestToken]: createTokenTester('xtest'),
|
|
108
|
+
};
|
|
86
109
|
|
|
87
110
|
/**
|
|
88
|
-
* Check and report statements for `
|
|
111
|
+
* Check and report statements for `PaddingType.Always` configuration.
|
|
89
112
|
* This autofix inserts a blank line between the given 2 statements.
|
|
90
113
|
* If the `prevNode` has trailing comments, it inserts a blank line after the
|
|
91
114
|
* trailing comments.
|
|
92
115
|
*/
|
|
93
|
-
|
|
94
|
-
context: Rule.RuleContext,
|
|
116
|
+
const paddingAlwaysTester = (
|
|
95
117
|
prevNode: Node,
|
|
96
118
|
nextNode: Node,
|
|
97
|
-
|
|
98
|
-
): void {
|
|
119
|
+
paddingContext: PaddingContext,
|
|
120
|
+
): void => {
|
|
121
|
+
const { sourceCode, ruleContext } = paddingContext;
|
|
122
|
+
const paddingLines = astUtils.getPaddingLineSequences(
|
|
123
|
+
prevNode,
|
|
124
|
+
nextNode,
|
|
125
|
+
sourceCode,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// We've got some padding lines. Great.
|
|
99
129
|
if (paddingLines.length > 0) {
|
|
100
130
|
return;
|
|
101
131
|
}
|
|
102
132
|
|
|
103
|
-
|
|
133
|
+
// Missing padding line
|
|
134
|
+
ruleContext.report({
|
|
104
135
|
node: nextNode,
|
|
105
136
|
message: 'Expected blank line before this statement.',
|
|
106
137
|
fix(fixer: Rule.RuleFixer): Rule.Fix {
|
|
107
|
-
|
|
108
|
-
let prevToken = getActualLastToken(sourceCode, prevNode);
|
|
138
|
+
let prevToken = astUtils.getActualLastToken(sourceCode, prevNode);
|
|
109
139
|
const nextToken =
|
|
110
140
|
sourceCode.getFirstTokenBetween(prevToken, nextNode, {
|
|
111
141
|
includeComments: true,
|
|
112
|
-
|
|
113
142
|
/**
|
|
114
143
|
* Skip the trailing comments of the previous node.
|
|
115
144
|
* This inserts a blank line after the last trailing comment.
|
|
@@ -128,7 +157,7 @@ function verifyForAlways(
|
|
|
128
157
|
* bar();
|
|
129
158
|
*/
|
|
130
159
|
filter(token: AST.Token): boolean {
|
|
131
|
-
if (
|
|
160
|
+
if (astUtils.areTokensOnSameLine(prevToken, token)) {
|
|
132
161
|
prevToken = token;
|
|
133
162
|
return false;
|
|
134
163
|
}
|
|
@@ -137,203 +166,185 @@ function verifyForAlways(
|
|
|
137
166
|
},
|
|
138
167
|
}) || nextNode;
|
|
139
168
|
|
|
140
|
-
const insertText =
|
|
169
|
+
const insertText = astUtils.areTokensOnSameLine(prevToken, nextToken)
|
|
141
170
|
? '\n\n'
|
|
142
171
|
: '\n';
|
|
143
172
|
|
|
144
173
|
return fixer.insertTextAfter(prevToken, insertText);
|
|
145
174
|
},
|
|
146
175
|
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Types of blank lines.
|
|
151
|
-
* `any` and `always` are defined.
|
|
152
|
-
* Those have `verify` method to check and report statements.
|
|
153
|
-
*/
|
|
154
|
-
const PaddingTypes = {
|
|
155
|
-
any: { verify: () => {} },
|
|
156
|
-
always: { verify: verifyForAlways },
|
|
157
176
|
};
|
|
158
177
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const StatementTypes = {
|
|
164
|
-
'*': { test: () => true },
|
|
165
|
-
afterAll: newJestTokenTester('afterAll'),
|
|
166
|
-
afterEach: newJestTokenTester('afterEach'),
|
|
167
|
-
beforeAll: newJestTokenTester('beforeAll'),
|
|
168
|
-
beforeEach: newJestTokenTester('beforeEach'),
|
|
169
|
-
describe: newJestTokenTester('describe'),
|
|
170
|
-
expect: newJestTokenTester('expect'),
|
|
171
|
-
it: newJestTokenTester('it'),
|
|
172
|
-
test: newJestTokenTester('test'),
|
|
178
|
+
// A mapping of PaddingType to PaddingTester
|
|
179
|
+
const paddingTesters: { [T in PaddingType]: PaddingTester } = {
|
|
180
|
+
[PaddingType.Any]: () => true,
|
|
181
|
+
[PaddingType.Always]: paddingAlwaysTester,
|
|
173
182
|
};
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
184
|
+
const createScopeInfo = (): ScopeInfo => {
|
|
185
|
+
return (() => {
|
|
186
|
+
let scope: Scope = null;
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
schema: {
|
|
183
|
-
definitions: {
|
|
184
|
-
paddingType: {
|
|
185
|
-
enum: Object.keys(PaddingTypes),
|
|
186
|
-
},
|
|
187
|
-
statementType: {
|
|
188
|
-
anyOf: [
|
|
189
|
-
{ enum: Object.keys(StatementTypes) },
|
|
190
|
-
{
|
|
191
|
-
type: 'array',
|
|
192
|
-
items: { enum: Object.keys(StatementTypes) },
|
|
193
|
-
minItems: 1,
|
|
194
|
-
uniqueItems: true,
|
|
195
|
-
additionalItems: false,
|
|
196
|
-
},
|
|
197
|
-
],
|
|
198
|
-
},
|
|
188
|
+
return {
|
|
189
|
+
get prevNode() {
|
|
190
|
+
return scope.prevNode;
|
|
199
191
|
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
type: 'object',
|
|
203
|
-
properties: {
|
|
204
|
-
blankLine: { $ref: '#/definitions/paddingType' },
|
|
205
|
-
prev: { $ref: '#/definitions/statementType' },
|
|
206
|
-
next: { $ref: '#/definitions/statementType' },
|
|
207
|
-
},
|
|
208
|
-
additionalProperties: false,
|
|
209
|
-
required: ['blankLine', 'prev', 'next'],
|
|
192
|
+
set prevNode(node) {
|
|
193
|
+
scope.prevNode = node;
|
|
210
194
|
},
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const sourceCode = context.getSourceCode();
|
|
222
|
-
const configureList = context.options || [];
|
|
223
|
-
let scopeInfo = null;
|
|
224
|
-
|
|
225
|
-
function enterScope(): void {
|
|
226
|
-
scopeInfo = { upper: scopeInfo, prevNode: null };
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function exitScope(): void {
|
|
230
|
-
scopeInfo = scopeInfo.upper;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Checks whether the given node matches the given type.
|
|
235
|
-
*/
|
|
236
|
-
function match(node: Node, type: string | string[]): boolean {
|
|
237
|
-
let innerStatementNode = node;
|
|
238
|
-
|
|
239
|
-
while (innerStatementNode.type === 'LabeledStatement') {
|
|
240
|
-
innerStatementNode = innerStatementNode.body;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (Array.isArray(type)) {
|
|
244
|
-
return type.some(match.bind(null, innerStatementNode));
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return StatementTypes[type].test(innerStatementNode, sourceCode);
|
|
248
|
-
}
|
|
195
|
+
enter() {
|
|
196
|
+
scope = { upper: scope, prevNode: null };
|
|
197
|
+
},
|
|
198
|
+
exit() {
|
|
199
|
+
scope = scope.upper;
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
})();
|
|
203
|
+
};
|
|
249
204
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Check whether the given node matches the statement type
|
|
207
|
+
*/
|
|
208
|
+
const nodeMatchesType = (
|
|
209
|
+
node: Node,
|
|
210
|
+
statementType: StatementTypes,
|
|
211
|
+
paddingContext: PaddingContext,
|
|
212
|
+
): boolean => {
|
|
213
|
+
let innerStatementNode = node;
|
|
214
|
+
const { sourceCode } = paddingContext;
|
|
215
|
+
|
|
216
|
+
// Dig into LabeledStatement body until it's not that anymore
|
|
217
|
+
while (innerStatementNode.type === 'LabeledStatement') {
|
|
218
|
+
innerStatementNode = innerStatementNode.body;
|
|
219
|
+
}
|
|
256
220
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
221
|
+
// If it's an array recursively check if any of the statement types match
|
|
222
|
+
// the node
|
|
223
|
+
if (Array.isArray(statementType)) {
|
|
224
|
+
return statementType.some((type) =>
|
|
225
|
+
nodeMatchesType(innerStatementNode, type, paddingContext),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
261
228
|
|
|
262
|
-
|
|
263
|
-
|
|
229
|
+
return statementTesters[statementType](innerStatementNode, sourceCode);
|
|
230
|
+
};
|
|
264
231
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return
|
|
232
|
+
/**
|
|
233
|
+
* Executes matching padding tester for last matched padding config for given
|
|
234
|
+
* nodes
|
|
235
|
+
*/
|
|
236
|
+
const testPadding = (
|
|
237
|
+
prevNode: Node,
|
|
238
|
+
nextNode: Node,
|
|
239
|
+
paddingContext: PaddingContext,
|
|
240
|
+
): void => {
|
|
241
|
+
const { configs } = paddingContext;
|
|
242
|
+
|
|
243
|
+
const testType = (type: PaddingType) =>
|
|
244
|
+
paddingTesters[type](prevNode, nextNode, paddingContext);
|
|
245
|
+
|
|
246
|
+
for (let i = configs.length - 1; i >= 0; --i) {
|
|
247
|
+
const {
|
|
248
|
+
prevStatementType: prevType,
|
|
249
|
+
nextStatementType: nextType,
|
|
250
|
+
paddingType,
|
|
251
|
+
} = configs[i];
|
|
252
|
+
|
|
253
|
+
if (
|
|
254
|
+
nodeMatchesType(prevNode, prevType, paddingContext) &&
|
|
255
|
+
nodeMatchesType(nextNode, nextType, paddingContext)
|
|
256
|
+
) {
|
|
257
|
+
return testType(paddingType);
|
|
291
258
|
}
|
|
259
|
+
}
|
|
292
260
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const parentType = node.parent.type;
|
|
298
|
-
const validParent = STATEMENT_LIST_PARENTS.has(parentType);
|
|
299
|
-
|
|
300
|
-
if (!validParent) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
261
|
+
// There were no matching padding rules for the prevNode, nextNode,
|
|
262
|
+
// paddingType combination... so we'll use PaddingType.Any which is always ok
|
|
263
|
+
return testType(PaddingType.Any);
|
|
264
|
+
};
|
|
303
265
|
|
|
304
|
-
|
|
305
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Verify padding lines between the given node and the previous node.
|
|
268
|
+
*/
|
|
269
|
+
const verifyNode = (node: Node, paddingContext: PaddingContext): void => {
|
|
270
|
+
const { scopeInfo } = paddingContext;
|
|
271
|
+
|
|
272
|
+
// NOTE: ESLint types use ESTree which provides a Node type, however
|
|
273
|
+
// ESTree.Node doesn't support the parent property which is added by
|
|
274
|
+
// ESLint during traversal. Our best bet is to ignore the property access
|
|
275
|
+
// here as it's the only place that it's checked.
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
277
|
+
if (!astUtils.isValidParent((node as any).parent.type)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
306
280
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const paddingLines = getPaddingLineSequences(prevNode, node);
|
|
281
|
+
if (scopeInfo.prevNode) {
|
|
282
|
+
testPadding(scopeInfo.prevNode, node, paddingContext);
|
|
283
|
+
}
|
|
311
284
|
|
|
312
|
-
|
|
313
|
-
|
|
285
|
+
scopeInfo.prevNode = node;
|
|
286
|
+
};
|
|
314
287
|
|
|
315
|
-
|
|
316
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Creates an ESLint rule for a given set of padding Config objects.
|
|
290
|
+
*
|
|
291
|
+
* The algorithm is approximately this:
|
|
292
|
+
*
|
|
293
|
+
* For each 'scope' in the program
|
|
294
|
+
* - Enter the scope (store the parent scope and previous node)
|
|
295
|
+
* - For each statement in the scope
|
|
296
|
+
* - Check the current node and previous node against the Config objects
|
|
297
|
+
* - If the current node and previous node match a Config, check the padding.
|
|
298
|
+
* Otherwise, ignore it.
|
|
299
|
+
* - If the padding is missing (and required), report and fix
|
|
300
|
+
* - Store the current node as the previous
|
|
301
|
+
* - Repeat
|
|
302
|
+
* - Exit scope (return to parent scope and clear previous node)
|
|
303
|
+
*
|
|
304
|
+
* The items we're looking for with this rule are ExpressionStatement nodes
|
|
305
|
+
* where the first token is an Identifier with a name matching one of the Jest
|
|
306
|
+
* functions. It's not foolproof, of course, but it's probably good enough for
|
|
307
|
+
* almost all cases.
|
|
308
|
+
*
|
|
309
|
+
* The Config objects specify a padding type, a previous statement type, and a
|
|
310
|
+
* next statement type. Wildcard statement types and padding types are
|
|
311
|
+
* supported. The current node and previous node are checked against the
|
|
312
|
+
* statement types. If they match then the specified padding type is
|
|
313
|
+
* tested/enforced.
|
|
314
|
+
*
|
|
315
|
+
* See src/index.ts for examples of Config usage.
|
|
316
|
+
*/
|
|
317
|
+
export const createRule = (
|
|
318
|
+
configs: Config[],
|
|
319
|
+
deprecated = false,
|
|
320
|
+
): Rule.RuleModule => ({
|
|
321
|
+
meta: {
|
|
322
|
+
fixable: 'whitespace',
|
|
323
|
+
deprecated,
|
|
324
|
+
},
|
|
325
|
+
create(context: Rule.RuleContext) {
|
|
326
|
+
const paddingContext = {
|
|
327
|
+
ruleContext: context,
|
|
328
|
+
sourceCode: context.getSourceCode(),
|
|
329
|
+
scopeInfo: createScopeInfo(),
|
|
330
|
+
configs,
|
|
331
|
+
};
|
|
317
332
|
|
|
318
|
-
|
|
319
|
-
* Verify padding lines between the given node and the previous node.
|
|
320
|
-
* Then process to enter to new scope.
|
|
321
|
-
*/
|
|
322
|
-
function verifyThenEnterScope(node: Node): void {
|
|
323
|
-
verify(node);
|
|
324
|
-
enterScope();
|
|
325
|
-
}
|
|
333
|
+
const { scopeInfo } = paddingContext;
|
|
326
334
|
|
|
327
335
|
return {
|
|
328
|
-
Program:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
'
|
|
332
|
-
|
|
333
|
-
'SwitchStatement:exit':
|
|
334
|
-
':statement':
|
|
335
|
-
SwitchCase:
|
|
336
|
-
|
|
336
|
+
Program: scopeInfo.enter,
|
|
337
|
+
'Program:exit': scopeInfo.enter,
|
|
338
|
+
BlockStatement: scopeInfo.enter,
|
|
339
|
+
'BlockStatement:exit': scopeInfo.exit,
|
|
340
|
+
SwitchStatement: scopeInfo.enter,
|
|
341
|
+
'SwitchStatement:exit': scopeInfo.exit,
|
|
342
|
+
':statement': (node: Node) => verifyNode(node, paddingContext),
|
|
343
|
+
SwitchCase: (node: Node) => {
|
|
344
|
+
verifyNode(node, paddingContext);
|
|
345
|
+
scopeInfo.enter();
|
|
346
|
+
},
|
|
347
|
+
'SwitchCase:exit': scopeInfo.exit,
|
|
337
348
|
};
|
|
338
349
|
},
|
|
339
|
-
};
|
|
350
|
+
});
|
package/tests/catfood.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file should be IGNORED by jest-formatting due to the `overrides.files`
|
|
3
|
+
* value in `recommended` and `strict` config. This file does not match the
|
|
4
|
+
* globs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
8
|
+
|
|
9
|
+
beforeAll(() => {});
|
|
10
|
+
beforeEach(() => {});
|
|
11
|
+
afterAll(() => {});
|
|
12
|
+
afterEach(() => {});
|
|
13
|
+
describe('no padding on top', () => {
|
|
14
|
+
it('has no padding above', () => {
|
|
15
|
+
expect(true);
|
|
16
|
+
});
|
|
17
|
+
expect(true);
|
|
18
|
+
expect(1);
|
|
19
|
+
xit('has padding above', () => {
|
|
20
|
+
expect(true);
|
|
21
|
+
});
|
|
22
|
+
it('also has padding above but not below', () => {
|
|
23
|
+
expect(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
xdescribe('padding above', () => {
|
|
27
|
+
it('has no padding above or below', () => {
|
|
28
|
+
expect(true);
|
|
29
|
+
});
|
|
30
|
+
it('has no padding above or below', () => {
|
|
31
|
+
expect(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('padding above', () => {
|
|
35
|
+
it('has no padding above or below', () => {
|
|
36
|
+
expect(true);
|
|
37
|
+
});
|
|
38
|
+
it('has no padding above or below', () => {
|
|
39
|
+
expect(true);
|
|
40
|
+
});
|
|
41
|
+
});
|