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