lint-rules-alvin 1.2.1 → 2.0.1
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/.vscode/settings.json +8 -1
- package/eslint/configs/{astro.js → astro.ts} +2 -3
- package/eslint/configs/{base.js → base.ts} +2 -3
- package/eslint/configs/{custom.js → custom.ts} +10 -12
- package/eslint/configs/{importX.js → importX.ts} +13 -3
- package/eslint/configs/index.d.ts +11 -13
- package/eslint/configs/index.ts +10 -0
- package/eslint/configs/json.ts +7 -0
- package/eslint/configs/{markdown.js → markdown.ts} +2 -4
- package/eslint/configs/{reactHooks.js → reactHooks.ts} +2 -3
- package/eslint/configs/{stylistic.js → stylistic.ts} +2 -4
- package/eslint/configs/{sveltekit.js → sveltekit.ts} +4 -7
- package/eslint/configs/{typescript.js → typescript.ts} +4 -4
- package/eslint/custom_rules/{destructure-newline.js → destructure-newline.ts} +19 -14
- package/eslint/custom_rules/jsx-multiline-prop-newline.ts +245 -0
- package/eslint/custom_rules/max-chain-per-line.ts +320 -0
- package/eslint/custom_rules/multiline-array-accessor-newline.ts +249 -0
- package/eslint/custom_rules/{multiline-paren-newline.js → multiline-paren-newline.ts} +130 -65
- package/eslint/custom_rules/{newline-between-imports.js → newline-between-imports.ts} +16 -14
- package/eslint/custom_rules/unnamed-imports-last.ts +105 -0
- package/eslint/presets/{astroReactTs.js → astroReact.ts} +13 -11
- package/eslint/presets/index.d.ts +3 -11
- package/eslint/presets/index.ts +2 -0
- package/eslint/presets/{sveltekitTs.js → sveltekit.ts} +9 -7
- package/eslint.config.ts +19 -0
- package/package.json +51 -23
- package/test/eslint/destructure-newline.test.ts +21 -0
- package/test/eslint/jsx-multiline-prop-newline.test.ts +203 -0
- package/test/eslint/max-chain-per-line.test.ts +238 -0
- package/test/eslint/multiline-array-accessor-newline.test.ts +37 -0
- package/test/eslint/multiline-paren-newline.test.ts +87 -0
- package/test/eslint/newline-between-imports.test.ts +20 -0
- package/test/eslint/test-bootstrap.mjs +28 -0
- package/test/eslint/unnamed-imports-last.test.ts +25 -0
- package/tsconfig.json +25 -0
- package/tsconfig.test.json +18 -0
- package/eslint/configs/importXTs.js +0 -24
- package/eslint/configs/index.js +0 -11
- package/eslint/configs/json.js +0 -25
- package/eslint/custom_rules/jsx-multiline-prop-newline.js +0 -208
- package/eslint/custom_rules/max-chain-per-line.js +0 -253
- package/eslint/custom_rules/multiline-array-accessor-newline.js +0 -193
- package/eslint/custom_rules/unnamed-imports-last.js +0 -82
- package/eslint/presets/index.js +0 -2
- package/eslint.config.js +0 -17
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AST_NODE_TYPES,
|
|
3
|
+
ESLintUtils,
|
|
4
|
+
TSESTree
|
|
5
|
+
} from '@typescript-eslint/utils';
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* We use `any` for node parameters to keep the rule implementation
|
|
9
|
+
* lightweight for the TypeScript language server. Pragmatic runtime
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const maxChainPerLineRule = ESLintUtils.RuleCreator.withoutDocs({
|
|
14
|
+
meta: {
|
|
15
|
+
type: 'layout',
|
|
16
|
+
docs: { description: 'Require a newline after each item in a chain if it\'s too long.' },
|
|
17
|
+
fixable: 'whitespace',
|
|
18
|
+
schema: [
|
|
19
|
+
{
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
maxChain: {
|
|
23
|
+
type: 'integer',
|
|
24
|
+
minimum: 1
|
|
25
|
+
},
|
|
26
|
+
enforceSingleLine: { type: 'boolean' }
|
|
27
|
+
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
messages: {
|
|
33
|
+
expand: 'This call chain is too long and should be broken into multiple lines.',
|
|
34
|
+
collapse: 'Unexpected newline in chain.'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [
|
|
38
|
+
{
|
|
39
|
+
maxChain: 2,
|
|
40
|
+
enforceSingleLine: false
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
create(context, options) {
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
maxChain,
|
|
47
|
+
enforceSingleLine
|
|
48
|
+
} = options[0];
|
|
49
|
+
const sourceCode = context.sourceCode;
|
|
50
|
+
const processedChains: Set<TSESTree.Node> = new Set();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Traverses the AST from a given node upwards to find the outermost
|
|
54
|
+
* node of a continuous chain.
|
|
55
|
+
*/
|
|
56
|
+
function getOutermostChainNode(node: TSESTree.Node) {
|
|
57
|
+
|
|
58
|
+
let outermostNode = node;
|
|
59
|
+
while (outermostNode.parent) {
|
|
60
|
+
|
|
61
|
+
const parentNode = outermostNode.parent;
|
|
62
|
+
if (
|
|
63
|
+
(parentNode.type === AST_NODE_TYPES.MemberExpression && parentNode.object === outermostNode)
|
|
64
|
+
|| (parentNode.type === AST_NODE_TYPES.CallExpression && parentNode.callee === outermostNode)
|
|
65
|
+
|| (parentNode.type === AST_NODE_TYPES.TSNonNullExpression && parentNode.expression === outermostNode)
|
|
66
|
+
) {
|
|
67
|
+
|
|
68
|
+
outermostNode = parentNode;
|
|
69
|
+
|
|
70
|
+
} else {
|
|
71
|
+
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
return outermostNode;
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Deconstructs a chain into an array of its 'links'.
|
|
83
|
+
* A link is defined as a non-computed property access (`.prop`).
|
|
84
|
+
* Any subsequent CallExpressions or computed accesses (`[key]`) are
|
|
85
|
+
* considered part of that same link.
|
|
86
|
+
*/
|
|
87
|
+
function getChainLinks(node: TSESTree.Node): TSESTree.MemberExpressionNonComputedName[] {
|
|
88
|
+
|
|
89
|
+
const links: TSESTree.MemberExpressionNonComputedName[] = [];
|
|
90
|
+
let currentNode: TSESTree.Node | undefined = node;
|
|
91
|
+
|
|
92
|
+
while (
|
|
93
|
+
currentNode.type === AST_NODE_TYPES.MemberExpression
|
|
94
|
+
|| currentNode.type === AST_NODE_TYPES.CallExpression
|
|
95
|
+
|| currentNode.type === AST_NODE_TYPES.TSNonNullExpression
|
|
96
|
+
) {
|
|
97
|
+
|
|
98
|
+
if (currentNode.type === AST_NODE_TYPES.CallExpression) {
|
|
99
|
+
|
|
100
|
+
currentNode = currentNode.callee;
|
|
101
|
+
continue;
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (currentNode.type === AST_NODE_TYPES.TSNonNullExpression) {
|
|
106
|
+
|
|
107
|
+
currentNode = currentNode.expression;
|
|
108
|
+
continue;
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// MemberExpression
|
|
113
|
+
if (currentNode.computed) {
|
|
114
|
+
|
|
115
|
+
currentNode = currentNode.object;
|
|
116
|
+
|
|
117
|
+
} else {
|
|
118
|
+
|
|
119
|
+
links.unshift(currentNode);
|
|
120
|
+
currentNode = currentNode.object;
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return links;
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function checkChain(node: TSESTree.Node): void {
|
|
131
|
+
|
|
132
|
+
const outermostNode = getOutermostChainNode(node);
|
|
133
|
+
|
|
134
|
+
if (processedChains.has(outermostNode)) {
|
|
135
|
+
|
|
136
|
+
return;
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
processedChains.add(outermostNode);
|
|
140
|
+
|
|
141
|
+
const links = getChainLinks(outermostNode);
|
|
142
|
+
const chainCount = links.length;
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* If enforceSingleLine is enabled, allow single-link chains to be
|
|
146
|
+
* considered for collapsing, so they can be collapsed if needed.
|
|
147
|
+
* Otherwise require at least 2 links.
|
|
148
|
+
*/
|
|
149
|
+
const minChain = enforceSingleLine
|
|
150
|
+
? 1
|
|
151
|
+
: 2;
|
|
152
|
+
if (chainCount < minChain) {
|
|
153
|
+
|
|
154
|
+
return;
|
|
155
|
+
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const startLineIndex = outermostNode
|
|
159
|
+
.loc
|
|
160
|
+
.start
|
|
161
|
+
.line - 1;
|
|
162
|
+
const startLine = sourceCode.lines[startLineIndex] ?? '';
|
|
163
|
+
const match = (/^\s*/).exec(startLine);
|
|
164
|
+
const baseIndent = match
|
|
165
|
+
? match[0]
|
|
166
|
+
: '';
|
|
167
|
+
|
|
168
|
+
// --- Mode 1: Enforce newlines if chain is too long ---
|
|
169
|
+
if (chainCount > maxChain) {
|
|
170
|
+
|
|
171
|
+
for (const linkNode of links) {
|
|
172
|
+
|
|
173
|
+
// Find the dot separator for this link.
|
|
174
|
+
const separatorToken = sourceCode.getTokenBefore(
|
|
175
|
+
linkNode.property,
|
|
176
|
+
{ filter: (token) => token.value === '.' || token.value === '?.' }
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// If we couldn't find the separator token, bail out for safety.
|
|
180
|
+
if (!separatorToken) {
|
|
181
|
+
|
|
182
|
+
continue;
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// We only care about the dot's position relative to the start of its own link.
|
|
187
|
+
const previousNode = linkNode.object;
|
|
188
|
+
if (
|
|
189
|
+
separatorToken
|
|
190
|
+
.loc
|
|
191
|
+
.start
|
|
192
|
+
.line === previousNode
|
|
193
|
+
.loc
|
|
194
|
+
.end
|
|
195
|
+
.line
|
|
196
|
+
) {
|
|
197
|
+
|
|
198
|
+
context.report({
|
|
199
|
+
node: linkNode.property,
|
|
200
|
+
loc: separatorToken.loc,
|
|
201
|
+
messageId: 'expand',
|
|
202
|
+
fix: (fixer) => fixer.insertTextBefore(
|
|
203
|
+
separatorToken,
|
|
204
|
+
`\n${baseIndent}`
|
|
205
|
+
)
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
} else if (enforceSingleLine) {
|
|
213
|
+
|
|
214
|
+
// --- Mode 2: Enforce single line if chain is short enough and option is enabled ---
|
|
215
|
+
|
|
216
|
+
for (const linkNode of links) {
|
|
217
|
+
|
|
218
|
+
const previousNode = linkNode.object;
|
|
219
|
+
const separatorToken2 = sourceCode.getTokenBefore(
|
|
220
|
+
linkNode.property,
|
|
221
|
+
{ filter: (token) => token.value === '.' || token.value === '?.' }
|
|
222
|
+
);
|
|
223
|
+
if (!separatorToken2)
|
|
224
|
+
continue;
|
|
225
|
+
if (
|
|
226
|
+
separatorToken2
|
|
227
|
+
.loc
|
|
228
|
+
.start
|
|
229
|
+
.line > previousNode
|
|
230
|
+
.loc
|
|
231
|
+
.end
|
|
232
|
+
.line
|
|
233
|
+
) {
|
|
234
|
+
|
|
235
|
+
const from = previousNode.range[1];
|
|
236
|
+
const to = separatorToken2.range[0];
|
|
237
|
+
|
|
238
|
+
if (typeof from === 'number' && typeof to === 'number') {
|
|
239
|
+
|
|
240
|
+
context.report({
|
|
241
|
+
node: linkNode.property,
|
|
242
|
+
loc: separatorToken2.loc,
|
|
243
|
+
messageId: 'collapse',
|
|
244
|
+
fix: (fixer) => {
|
|
245
|
+
|
|
246
|
+
const between = sourceCode.text.slice(
|
|
247
|
+
from,
|
|
248
|
+
to
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// If there are non-whitespace chars (comments) inside, skip fixing.
|
|
252
|
+
if ((/\S/).test(between)) {
|
|
253
|
+
|
|
254
|
+
return null;
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const segments = between.split(
|
|
259
|
+
/\r\n|\r|\n/
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
let collapsed = '';
|
|
263
|
+
|
|
264
|
+
if (segments.length === 1) {
|
|
265
|
+
|
|
266
|
+
collapsed = segments[0] ?? '';
|
|
267
|
+
|
|
268
|
+
} else {
|
|
269
|
+
|
|
270
|
+
const firstSegment = segments[0] ?? '';
|
|
271
|
+
const lastSegment = segments[segments.length - 1] ?? '';
|
|
272
|
+
|
|
273
|
+
collapsed = `${firstSegment}${lastSegment}`;
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return fixer.replaceTextRange(
|
|
278
|
+
[
|
|
279
|
+
from,
|
|
280
|
+
to
|
|
281
|
+
],
|
|
282
|
+
collapsed
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
} else {
|
|
289
|
+
|
|
290
|
+
context.report({
|
|
291
|
+
node: linkNode.property,
|
|
292
|
+
loc: separatorToken2.loc,
|
|
293
|
+
messageId: 'collapse'
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
MemberExpression(node) {
|
|
308
|
+
|
|
309
|
+
// Only start the check from non-computed MemberExpressions to avoid redundant checks.
|
|
310
|
+
if (!node.computed) {
|
|
311
|
+
|
|
312
|
+
checkChain(node);
|
|
313
|
+
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
});
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ESLintUtils,
|
|
3
|
+
TSESTree
|
|
4
|
+
} from '@typescript-eslint/utils';
|
|
5
|
+
|
|
6
|
+
export const multilineArrayAccessorNewlineRule = ESLintUtils.RuleCreator.withoutDocs({
|
|
7
|
+
meta: {
|
|
8
|
+
type: 'layout',
|
|
9
|
+
docs: { description: 'Require newlines inside multiline array accessors' },
|
|
10
|
+
fixable: 'whitespace',
|
|
11
|
+
schema: [],
|
|
12
|
+
messages: {
|
|
13
|
+
expandAfter: 'Expected a newline after \'\'',
|
|
14
|
+
expandBefore: 'Expected a newline before \'\']\'',
|
|
15
|
+
collapse: 'Array accessor content should be on a single line.'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
|
|
21
|
+
const sourceCode = context.sourceCode;
|
|
22
|
+
|
|
23
|
+
function checkMemberExpression(node: TSESTree.MemberExpression) {
|
|
24
|
+
|
|
25
|
+
const openBracket = sourceCode.getTokenAfter(
|
|
26
|
+
node.object,
|
|
27
|
+
{ filter: (t) => t.value === '[' }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (!openBracket) {
|
|
31
|
+
|
|
32
|
+
return;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const closeBracket = sourceCode.getLastToken(node);
|
|
37
|
+
|
|
38
|
+
if (closeBracket?.value !== ']') {
|
|
39
|
+
|
|
40
|
+
return;
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If the brackets are on the same line, this rule doesn't apply.
|
|
45
|
+
if (
|
|
46
|
+
openBracket
|
|
47
|
+
.loc
|
|
48
|
+
.start
|
|
49
|
+
.line === closeBracket
|
|
50
|
+
.loc
|
|
51
|
+
.end
|
|
52
|
+
.line
|
|
53
|
+
) {
|
|
54
|
+
|
|
55
|
+
return;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const firstTokenOfProperty = sourceCode.getFirstToken(node.property);
|
|
60
|
+
const lastTokenOfProperty = sourceCode.getLastToken(node.property);
|
|
61
|
+
|
|
62
|
+
if (!firstTokenOfProperty || !lastTokenOfProperty) {
|
|
63
|
+
|
|
64
|
+
return;
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if property is on a single line
|
|
69
|
+
if (
|
|
70
|
+
firstTokenOfProperty
|
|
71
|
+
.loc
|
|
72
|
+
.start
|
|
73
|
+
.line === lastTokenOfProperty
|
|
74
|
+
.loc
|
|
75
|
+
.end
|
|
76
|
+
.line
|
|
77
|
+
) {
|
|
78
|
+
|
|
79
|
+
const openBracketOnOwnLine = openBracket
|
|
80
|
+
.loc
|
|
81
|
+
.end
|
|
82
|
+
.line !== firstTokenOfProperty
|
|
83
|
+
.loc
|
|
84
|
+
.start
|
|
85
|
+
.line;
|
|
86
|
+
const closeBracketOnOwnLine = lastTokenOfProperty
|
|
87
|
+
.loc
|
|
88
|
+
.end
|
|
89
|
+
.line !== closeBracket
|
|
90
|
+
.loc
|
|
91
|
+
.start
|
|
92
|
+
.line;
|
|
93
|
+
|
|
94
|
+
if (openBracketOnOwnLine || closeBracketOnOwnLine) {
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* Report separate errors for opening and closing brackets so each
|
|
98
|
+
* bracket produces its own message and fix.
|
|
99
|
+
*/
|
|
100
|
+
if (openBracketOnOwnLine) {
|
|
101
|
+
|
|
102
|
+
context.report({
|
|
103
|
+
node: openBracket,
|
|
104
|
+
messageId: 'collapse',
|
|
105
|
+
fix(fixer) {
|
|
106
|
+
|
|
107
|
+
const start = openBracket.range[1];
|
|
108
|
+
const end = firstTokenOfProperty.range[0];
|
|
109
|
+
const between = sourceCode.text.slice(
|
|
110
|
+
start,
|
|
111
|
+
end
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// No fixer when comments inside
|
|
115
|
+
if ((/\S/).test(between) && !(/^\s*$/).test(between)) {
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Preserve any spaces that are on the same line as the first token.
|
|
122
|
+
const lastNewline = between.lastIndexOf('\n');
|
|
123
|
+
const preserve = lastNewline >= 0
|
|
124
|
+
? between.slice(lastNewline + 1)
|
|
125
|
+
: between;
|
|
126
|
+
|
|
127
|
+
return fixer.replaceTextRange(
|
|
128
|
+
[
|
|
129
|
+
start,
|
|
130
|
+
end
|
|
131
|
+
],
|
|
132
|
+
preserve
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (closeBracketOnOwnLine) {
|
|
141
|
+
|
|
142
|
+
context.report({
|
|
143
|
+
node: closeBracket,
|
|
144
|
+
messageId: 'collapse',
|
|
145
|
+
fix(fixer) {
|
|
146
|
+
|
|
147
|
+
const start = lastTokenOfProperty.range[1];
|
|
148
|
+
const end = closeBracket.range[0];
|
|
149
|
+
const between = sourceCode.text.slice(
|
|
150
|
+
start,
|
|
151
|
+
end
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// If there are non-whitespace chars (comments) inside, skip fixing.
|
|
155
|
+
if ((/\S/).test(between) && !(/^\s*$/).test(between)) {
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Preserve any spaces that are on the same line as the last token.
|
|
162
|
+
const firstNewline = between.indexOf('\n');
|
|
163
|
+
const preserve = firstNewline >= 0
|
|
164
|
+
? between.slice(
|
|
165
|
+
0,
|
|
166
|
+
firstNewline
|
|
167
|
+
)
|
|
168
|
+
: between;
|
|
169
|
+
|
|
170
|
+
return fixer.replaceTextRange(
|
|
171
|
+
[
|
|
172
|
+
start,
|
|
173
|
+
end
|
|
174
|
+
],
|
|
175
|
+
preserve
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for a newline after the opening bracket.
|
|
189
|
+
if (
|
|
190
|
+
openBracket
|
|
191
|
+
.loc
|
|
192
|
+
.end
|
|
193
|
+
.line === firstTokenOfProperty
|
|
194
|
+
.loc
|
|
195
|
+
.start
|
|
196
|
+
.line
|
|
197
|
+
) {
|
|
198
|
+
|
|
199
|
+
context.report({
|
|
200
|
+
node: openBracket,
|
|
201
|
+
messageId: 'expandAfter',
|
|
202
|
+
fix: (fixer) => fixer.insertTextAfter(
|
|
203
|
+
openBracket,
|
|
204
|
+
'\n'
|
|
205
|
+
)
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check for a newline before the closing bracket.
|
|
211
|
+
if (
|
|
212
|
+
lastTokenOfProperty
|
|
213
|
+
.loc
|
|
214
|
+
.end
|
|
215
|
+
.line === closeBracket
|
|
216
|
+
.loc
|
|
217
|
+
.start
|
|
218
|
+
.line
|
|
219
|
+
) {
|
|
220
|
+
|
|
221
|
+
context.report({
|
|
222
|
+
node: closeBracket,
|
|
223
|
+
messageId: 'expandBefore',
|
|
224
|
+
fix: (fixer) => fixer.insertTextBefore(
|
|
225
|
+
closeBracket,
|
|
226
|
+
'\n'
|
|
227
|
+
)
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
MemberExpression(node) {
|
|
236
|
+
|
|
237
|
+
if (!node.computed) {
|
|
238
|
+
|
|
239
|
+
return;
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
checkMemberExpression(node);
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
}
|
|
249
|
+
});
|