eslint 6.0.1 → 6.2.2
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/CHANGELOG.md +77 -0
- package/README.md +8 -5
- package/bin/eslint.js +3 -0
- package/conf/config-schema.js +1 -0
- package/conf/environments.js +72 -15
- package/lib/cli-engine/cli-engine.js +5 -2
- package/lib/cli-engine/config-array/config-array.js +7 -0
- package/lib/cli-engine/config-array/extracted-config.js +16 -1
- package/lib/cli-engine/config-array-factory.js +3 -1
- package/lib/cli-engine/file-enumerator.js +5 -13
- package/lib/init/config-initializer.js +19 -9
- package/lib/init/npm-utils.js +2 -2
- package/lib/linter/code-path-analysis/code-path-analyzer.js +1 -0
- package/lib/linter/linter.js +49 -16
- package/lib/rule-tester/rule-tester.js +1 -1
- package/lib/rules/accessor-pairs.js +195 -35
- package/lib/rules/arrow-body-style.js +2 -2
- package/lib/rules/class-methods-use-this.js +10 -3
- package/lib/rules/dot-location.js +21 -17
- package/lib/rules/dot-notation.js +6 -2
- package/lib/rules/func-call-spacing.js +30 -20
- package/lib/rules/func-names.js +4 -0
- package/lib/rules/function-call-argument-newline.js +120 -0
- package/lib/rules/function-paren-newline.js +34 -22
- package/lib/rules/indent.js +13 -2
- package/lib/rules/index.js +1 -0
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/no-dupe-keys.js +1 -1
- package/lib/rules/no-duplicate-case.js +10 -8
- package/lib/rules/no-extra-bind.js +1 -0
- package/lib/rules/no-extra-boolean-cast.js +44 -5
- package/lib/rules/no-extra-parens.js +295 -39
- package/lib/rules/no-mixed-operators.js +48 -13
- package/lib/rules/no-param-reassign.js +12 -1
- package/lib/rules/no-restricted-syntax.js +2 -2
- package/lib/rules/no-unused-vars.js +1 -1
- package/lib/rules/prefer-const.js +9 -3
- package/lib/rules/prefer-template.js +1 -10
- package/lib/rules/sort-keys.js +11 -3
- package/lib/rules/utils/ast-utils.js +45 -3
- package/lib/rules/yoda.js +1 -1
- package/lib/{cli-engine → shared}/naming.js +0 -0
- package/lib/shared/types.js +2 -0
- package/package.json +22 -21
@@ -549,8 +549,8 @@ class RuleTester {
|
|
549
549
|
assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
|
550
550
|
}
|
551
551
|
assert.strictEqual(
|
552
|
-
error.messageId,
|
553
552
|
message.messageId,
|
553
|
+
error.messageId,
|
554
554
|
`messageId '${message.messageId}' does not match expected messageId '${error.messageId}'.`
|
555
555
|
);
|
556
556
|
if (hasOwnProperty(error, "data")) {
|
@@ -5,10 +5,87 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Typedefs
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Property name if it can be computed statically, otherwise the list of the tokens of the key node.
|
20
|
+
* @typedef {string|Token[]} Key
|
21
|
+
*/
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Accessor nodes with the same key.
|
25
|
+
* @typedef {Object} AccessorData
|
26
|
+
* @property {Key} key Accessor's key
|
27
|
+
* @property {ASTNode[]} getters List of getter nodes.
|
28
|
+
* @property {ASTNode[]} setters List of setter nodes.
|
29
|
+
*/
|
30
|
+
|
8
31
|
//------------------------------------------------------------------------------
|
9
32
|
// Helpers
|
10
33
|
//------------------------------------------------------------------------------
|
11
34
|
|
35
|
+
/**
|
36
|
+
* Checks whether or not the given lists represent the equal tokens in the same order.
|
37
|
+
* Tokens are compared by their properties, not by instance.
|
38
|
+
* @param {Token[]} left First list of tokens.
|
39
|
+
* @param {Token[]} right Second list of tokens.
|
40
|
+
* @returns {boolean} `true` if the lists have same tokens.
|
41
|
+
*/
|
42
|
+
function areEqualTokenLists(left, right) {
|
43
|
+
if (left.length !== right.length) {
|
44
|
+
return false;
|
45
|
+
}
|
46
|
+
|
47
|
+
for (let i = 0; i < left.length; i++) {
|
48
|
+
const leftToken = left[i],
|
49
|
+
rightToken = right[i];
|
50
|
+
|
51
|
+
if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Checks whether or not the given keys are equal.
|
61
|
+
* @param {Key} left First key.
|
62
|
+
* @param {Key} right Second key.
|
63
|
+
* @returns {boolean} `true` if the keys are equal.
|
64
|
+
*/
|
65
|
+
function areEqualKeys(left, right) {
|
66
|
+
if (typeof left === "string" && typeof right === "string") {
|
67
|
+
|
68
|
+
// Statically computed names.
|
69
|
+
return left === right;
|
70
|
+
}
|
71
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
72
|
+
|
73
|
+
// Token lists.
|
74
|
+
return areEqualTokenLists(left, right);
|
75
|
+
}
|
76
|
+
|
77
|
+
return false;
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Checks whether or not a given node is of an accessor kind ('get' or 'set').
|
82
|
+
* @param {ASTNode} node - A node to check.
|
83
|
+
* @returns {boolean} `true` if the node is of an accessor kind.
|
84
|
+
*/
|
85
|
+
function isAccessorKind(node) {
|
86
|
+
return node.kind === "get" || node.kind === "set";
|
87
|
+
}
|
88
|
+
|
12
89
|
/**
|
13
90
|
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
14
91
|
* @param {ASTNode} node - A node to check.
|
@@ -97,69 +174,152 @@ module.exports = {
|
|
97
174
|
}],
|
98
175
|
|
99
176
|
messages: {
|
100
|
-
|
101
|
-
|
177
|
+
missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.",
|
178
|
+
missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.",
|
179
|
+
missingGetterInObjectLiteral: "Getter is not present for {{ name }}.",
|
180
|
+
missingSetterInObjectLiteral: "Setter is not present for {{ name }}."
|
102
181
|
}
|
103
182
|
},
|
104
183
|
create(context) {
|
105
184
|
const config = context.options[0] || {};
|
106
185
|
const checkGetWithoutSet = config.getWithoutSet === true;
|
107
186
|
const checkSetWithoutGet = config.setWithoutGet !== false;
|
187
|
+
const sourceCode = context.getSourceCode();
|
108
188
|
|
109
189
|
/**
|
110
|
-
*
|
111
|
-
* @param {ASTNode} node The node to
|
190
|
+
* Reports the given node.
|
191
|
+
* @param {ASTNode} node The node to report.
|
192
|
+
* @param {string} messageKind "missingGetter" or "missingSetter".
|
112
193
|
* @returns {void}
|
113
194
|
* @private
|
114
195
|
*/
|
115
|
-
function
|
116
|
-
|
117
|
-
|
118
|
-
|
196
|
+
function report(node, messageKind) {
|
197
|
+
if (node.type === "Property") {
|
198
|
+
context.report({
|
199
|
+
node,
|
200
|
+
messageId: `${messageKind}InObjectLiteral`,
|
201
|
+
loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
|
202
|
+
data: { name: astUtils.getFunctionNameWithKind(node.value) }
|
203
|
+
});
|
204
|
+
} else {
|
205
|
+
context.report({
|
206
|
+
node,
|
207
|
+
messageId: `${messageKind}InPropertyDescriptor`
|
208
|
+
});
|
209
|
+
}
|
210
|
+
}
|
119
211
|
|
120
|
-
|
121
|
-
|
212
|
+
/**
|
213
|
+
* Reports each of the nodes in the given list using the same messageId.
|
214
|
+
* @param {ASTNode[]} nodes Nodes to report.
|
215
|
+
* @param {string} messageKind "missingGetter" or "missingSetter".
|
216
|
+
* @returns {void}
|
217
|
+
* @private
|
218
|
+
*/
|
219
|
+
function reportList(nodes, messageKind) {
|
220
|
+
for (const node of nodes) {
|
221
|
+
report(node, messageKind);
|
222
|
+
}
|
223
|
+
}
|
122
224
|
|
123
|
-
|
225
|
+
/**
|
226
|
+
* Creates a new `AccessorData` object for the given getter or setter node.
|
227
|
+
* @param {ASTNode} node A getter or setter node.
|
228
|
+
* @returns {AccessorData} New `AccessorData` object that contains the given node.
|
229
|
+
* @private
|
230
|
+
*/
|
231
|
+
function createAccessorData(node) {
|
232
|
+
const name = astUtils.getStaticPropertyName(node);
|
233
|
+
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
|
124
234
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
235
|
+
return {
|
236
|
+
key,
|
237
|
+
getters: node.kind === "get" ? [node] : [],
|
238
|
+
setters: node.kind === "set" ? [node] : []
|
239
|
+
};
|
240
|
+
}
|
132
241
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
242
|
+
/**
|
243
|
+
* Merges the given `AccessorData` object into the given accessors list.
|
244
|
+
* @param {AccessorData[]} accessors The list to merge into.
|
245
|
+
* @param {AccessorData} accessorData The object to merge.
|
246
|
+
* @returns {AccessorData[]} The same instance with the merged object.
|
247
|
+
* @private
|
248
|
+
*/
|
249
|
+
function mergeAccessorData(accessors, accessorData) {
|
250
|
+
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
|
137
251
|
|
138
|
-
|
139
|
-
|
140
|
-
|
252
|
+
if (equalKeyElement) {
|
253
|
+
equalKeyElement.getters.push(...accessorData.getters);
|
254
|
+
equalKeyElement.setters.push(...accessorData.setters);
|
255
|
+
} else {
|
256
|
+
accessors.push(accessorData);
|
257
|
+
}
|
141
258
|
|
142
|
-
|
259
|
+
return accessors;
|
260
|
+
}
|
143
261
|
|
144
|
-
|
145
|
-
|
262
|
+
/**
|
263
|
+
* Checks accessor pairs in the given list of nodes.
|
264
|
+
* @param {ASTNode[]} nodes The list to check.
|
265
|
+
* @returns {void}
|
266
|
+
* @private
|
267
|
+
*/
|
268
|
+
function checkList(nodes) {
|
269
|
+
const accessors = nodes
|
270
|
+
.filter(isAccessorKind)
|
271
|
+
.map(createAccessorData)
|
272
|
+
.reduce(mergeAccessorData, []);
|
146
273
|
|
147
|
-
|
148
|
-
|
274
|
+
for (const { getters, setters } of accessors) {
|
275
|
+
if (checkSetWithoutGet && setters.length && !getters.length) {
|
276
|
+
reportList(setters, "missingGetter");
|
277
|
+
}
|
278
|
+
if (checkGetWithoutSet && getters.length && !setters.length) {
|
279
|
+
reportList(getters, "missingSetter");
|
149
280
|
}
|
150
281
|
}
|
282
|
+
}
|
151
283
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
284
|
+
/**
|
285
|
+
* Checks accessor pairs in an object literal.
|
286
|
+
* @param {ASTNode} node `ObjectExpression` node to check.
|
287
|
+
* @returns {void}
|
288
|
+
* @private
|
289
|
+
*/
|
290
|
+
function checkObjectLiteral(node) {
|
291
|
+
checkList(node.properties.filter(p => p.type === "Property"));
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Checks accessor pairs in a property descriptor.
|
296
|
+
* @param {ASTNode} node Property descriptor `ObjectExpression` node to check.
|
297
|
+
* @returns {void}
|
298
|
+
* @private
|
299
|
+
*/
|
300
|
+
function checkPropertyDescriptor(node) {
|
301
|
+
const namesToCheck = node.properties
|
302
|
+
.filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
|
303
|
+
.map(({ key }) => key.name);
|
304
|
+
|
305
|
+
const hasGetter = namesToCheck.includes("get");
|
306
|
+
const hasSetter = namesToCheck.includes("set");
|
307
|
+
|
308
|
+
if (checkSetWithoutGet && hasSetter && !hasGetter) {
|
309
|
+
report(node, "missingGetter");
|
310
|
+
}
|
311
|
+
if (checkGetWithoutSet && hasGetter && !hasSetter) {
|
312
|
+
report(node, "missingSetter");
|
156
313
|
}
|
157
314
|
}
|
158
315
|
|
159
316
|
return {
|
160
317
|
ObjectExpression(node) {
|
161
318
|
if (checkSetWithoutGet || checkGetWithoutSet) {
|
162
|
-
|
319
|
+
checkObjectLiteral(node);
|
320
|
+
if (isPropertyDescriptor(node)) {
|
321
|
+
checkPropertyDescriptor(node);
|
322
|
+
}
|
163
323
|
}
|
164
324
|
}
|
165
325
|
};
|
@@ -175,10 +175,10 @@ module.exports = {
|
|
175
175
|
}
|
176
176
|
|
177
177
|
/*
|
178
|
-
* If the first token of the reutrn value is `{
|
178
|
+
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
|
179
179
|
* enclose the return value by parentheses to avoid syntax error.
|
180
180
|
*/
|
181
|
-
if (astUtils.isOpeningBraceToken(firstValueToken)) {
|
181
|
+
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
|
182
182
|
fixes.push(
|
183
183
|
fixer.insertTextBefore(firstValueToken, "("),
|
184
184
|
fixer.insertTextAfter(lastValueToken, ")")
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -34,7 +40,7 @@ module.exports = {
|
|
34
40
|
}],
|
35
41
|
|
36
42
|
messages: {
|
37
|
-
missingThis: "Expected 'this' to be used by class
|
43
|
+
missingThis: "Expected 'this' to be used by class {{name}}."
|
38
44
|
}
|
39
45
|
},
|
40
46
|
create(context) {
|
@@ -70,7 +76,8 @@ module.exports = {
|
|
70
76
|
* @private
|
71
77
|
*/
|
72
78
|
function isIncludedInstanceMethod(node) {
|
73
|
-
return isInstanceMethod(node) &&
|
79
|
+
return isInstanceMethod(node) &&
|
80
|
+
(node.computed || !exceptMethods.has(node.key.name));
|
74
81
|
}
|
75
82
|
|
76
83
|
/**
|
@@ -89,7 +96,7 @@ module.exports = {
|
|
89
96
|
node,
|
90
97
|
messageId: "missingThis",
|
91
98
|
data: {
|
92
|
-
name: node
|
99
|
+
name: astUtils.getFunctionNameWithKind(node)
|
93
100
|
}
|
94
101
|
});
|
95
102
|
}
|
@@ -54,29 +54,31 @@ module.exports = {
|
|
54
54
|
*/
|
55
55
|
function checkDotLocation(obj, prop, node) {
|
56
56
|
const dot = sourceCode.getTokenBefore(prop);
|
57
|
-
|
57
|
+
|
58
|
+
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
59
|
+
const tokenBeforeDot = sourceCode.getTokenBefore(dot);
|
60
|
+
|
61
|
+
const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
|
58
62
|
const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
|
59
63
|
|
60
|
-
if (
|
61
|
-
if (
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
context.report({
|
66
|
-
node,
|
67
|
-
loc: dot.loc.start,
|
68
|
-
messageId: "expectedDotAfterObject",
|
69
|
-
fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
|
70
|
-
});
|
71
|
-
}
|
72
|
-
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
64
|
+
if (onObject) {
|
65
|
+
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
|
66
|
+
const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
|
67
|
+
|
73
68
|
context.report({
|
74
69
|
node,
|
75
70
|
loc: dot.loc.start,
|
76
|
-
messageId: "
|
77
|
-
fix: fixer => fixer.replaceTextRange([
|
71
|
+
messageId: "expectedDotAfterObject",
|
72
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
|
78
73
|
});
|
79
74
|
}
|
75
|
+
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
76
|
+
context.report({
|
77
|
+
node,
|
78
|
+
loc: dot.loc.start,
|
79
|
+
messageId: "expectedDotBeforeProperty",
|
80
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
|
81
|
+
});
|
80
82
|
}
|
81
83
|
}
|
82
84
|
|
@@ -86,7 +88,9 @@ module.exports = {
|
|
86
88
|
* @returns {void}
|
87
89
|
*/
|
88
90
|
function checkNode(node) {
|
89
|
-
|
91
|
+
if (!node.computed) {
|
92
|
+
checkDotLocation(node.object, node.property, node);
|
93
|
+
}
|
90
94
|
}
|
91
95
|
|
92
96
|
return {
|
@@ -9,13 +9,16 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const astUtils = require("./utils/ast-utils");
|
12
|
+
const keywords = require("./utils/keywords");
|
12
13
|
|
13
14
|
//------------------------------------------------------------------------------
|
14
15
|
// Rule Definition
|
15
16
|
//------------------------------------------------------------------------------
|
16
17
|
|
17
18
|
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
|
18
|
-
|
19
|
+
|
20
|
+
// `null` literal must be handled separately.
|
21
|
+
const literalTypesToCheck = new Set(["string", "boolean"]);
|
19
22
|
|
20
23
|
module.exports = {
|
21
24
|
meta: {
|
@@ -115,7 +118,8 @@ module.exports = {
|
|
115
118
|
MemberExpression(node) {
|
116
119
|
if (
|
117
120
|
node.computed &&
|
118
|
-
node.property.type === "Literal"
|
121
|
+
node.property.type === "Literal" &&
|
122
|
+
(literalTypesToCheck.has(typeof node.property.value) || astUtils.isNullLiteral(node.property))
|
119
123
|
) {
|
120
124
|
checkComputedProperty(node, node.property.value);
|
121
125
|
}
|
@@ -78,21 +78,13 @@ module.exports = {
|
|
78
78
|
/**
|
79
79
|
* Check if open space is present in a function name
|
80
80
|
* @param {ASTNode} node node to evaluate
|
81
|
+
* @param {Token} leftToken The last token of the callee. This may be the closing parenthesis that encloses the callee.
|
82
|
+
* @param {Token} rightToken Tha first token of the arguments. this is the opening parenthesis that encloses the arguments.
|
81
83
|
* @returns {void}
|
82
84
|
* @private
|
83
85
|
*/
|
84
|
-
function checkSpacing(node) {
|
85
|
-
const
|
86
|
-
const lastCalleeToken = sourceCode.getLastToken(node.callee);
|
87
|
-
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
|
88
|
-
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
|
89
|
-
|
90
|
-
// Parens in NewExpression are optional
|
91
|
-
if (!(parenToken && parenToken.range[1] < node.range[1])) {
|
92
|
-
return;
|
93
|
-
}
|
94
|
-
|
95
|
-
const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//gu, "");
|
86
|
+
function checkSpacing(node, leftToken, rightToken) {
|
87
|
+
const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, "");
|
96
88
|
const hasWhitespace = /\s/u.test(textBetweenTokens);
|
97
89
|
const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
|
98
90
|
|
@@ -123,7 +115,7 @@ module.exports = {
|
|
123
115
|
if (never && hasWhitespace) {
|
124
116
|
context.report({
|
125
117
|
node,
|
126
|
-
loc:
|
118
|
+
loc: leftToken.loc.start,
|
127
119
|
messageId: "unexpected",
|
128
120
|
fix(fixer) {
|
129
121
|
|
@@ -132,7 +124,7 @@ module.exports = {
|
|
132
124
|
* https://github.com/eslint/eslint/issues/7787
|
133
125
|
*/
|
134
126
|
if (!hasNewline) {
|
135
|
-
return fixer.removeRange([
|
127
|
+
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
136
128
|
}
|
137
129
|
|
138
130
|
return null;
|
@@ -141,27 +133,45 @@ module.exports = {
|
|
141
133
|
} else if (!never && !hasWhitespace) {
|
142
134
|
context.report({
|
143
135
|
node,
|
144
|
-
loc:
|
136
|
+
loc: leftToken.loc.start,
|
145
137
|
messageId: "missing",
|
146
138
|
fix(fixer) {
|
147
|
-
return fixer.insertTextBefore(
|
139
|
+
return fixer.insertTextBefore(rightToken, " ");
|
148
140
|
}
|
149
141
|
});
|
150
142
|
} else if (!never && !allowNewlines && hasNewline) {
|
151
143
|
context.report({
|
152
144
|
node,
|
153
|
-
loc:
|
145
|
+
loc: leftToken.loc.start,
|
154
146
|
messageId: "unexpected",
|
155
147
|
fix(fixer) {
|
156
|
-
return fixer.replaceTextRange([
|
148
|
+
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
|
157
149
|
}
|
158
150
|
});
|
159
151
|
}
|
160
152
|
}
|
161
153
|
|
162
154
|
return {
|
163
|
-
CallExpression
|
164
|
-
|
155
|
+
"CallExpression, NewExpression"(node) {
|
156
|
+
const lastToken = sourceCode.getLastToken(node);
|
157
|
+
const lastCalleeToken = sourceCode.getLastToken(node.callee);
|
158
|
+
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
|
159
|
+
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
|
160
|
+
|
161
|
+
// Parens in NewExpression are optional
|
162
|
+
if (!(parenToken && parenToken.range[1] < node.range[1])) {
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
|
166
|
+
checkSpacing(node, prevToken, parenToken);
|
167
|
+
},
|
168
|
+
|
169
|
+
ImportExpression(node) {
|
170
|
+
const leftToken = sourceCode.getFirstToken(node);
|
171
|
+
const rightToken = sourceCode.getTokenAfter(leftToken);
|
172
|
+
|
173
|
+
checkSpacing(node, leftToken, rightToken);
|
174
|
+
}
|
165
175
|
};
|
166
176
|
|
167
177
|
}
|
package/lib/rules/func-names.js
CHANGED
@@ -69,6 +69,8 @@ module.exports = {
|
|
69
69
|
|
70
70
|
create(context) {
|
71
71
|
|
72
|
+
const sourceCode = context.getSourceCode();
|
73
|
+
|
72
74
|
/**
|
73
75
|
* Returns the config option for the given node.
|
74
76
|
* @param {ASTNode} node - A node to get the config for.
|
@@ -130,6 +132,7 @@ module.exports = {
|
|
130
132
|
context.report({
|
131
133
|
node,
|
132
134
|
messageId: "unnamed",
|
135
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
133
136
|
data: { name: astUtils.getFunctionNameWithKind(node) }
|
134
137
|
});
|
135
138
|
}
|
@@ -143,6 +146,7 @@ module.exports = {
|
|
143
146
|
context.report({
|
144
147
|
node,
|
145
148
|
messageId: "named",
|
149
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
146
150
|
data: { name: astUtils.getFunctionNameWithKind(node) }
|
147
151
|
});
|
148
152
|
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to enforce line breaks between arguments of a function call
|
3
|
+
* @author Alexey Gonchar <https://github.com/finico>
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Rule Definition
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "layout",
|
15
|
+
|
16
|
+
docs: {
|
17
|
+
description: "enforce line breaks between arguments of a function call",
|
18
|
+
category: "Stylistic Issues",
|
19
|
+
recommended: false,
|
20
|
+
url: "https://eslint.org/docs/rules/function-call-argument-newline"
|
21
|
+
},
|
22
|
+
|
23
|
+
fixable: "whitespace",
|
24
|
+
|
25
|
+
schema: [
|
26
|
+
{
|
27
|
+
enum: ["always", "never", "consistent"]
|
28
|
+
}
|
29
|
+
],
|
30
|
+
|
31
|
+
messages: {
|
32
|
+
unexpectedLineBreak: "There should be no line break here.",
|
33
|
+
missingLineBreak: "There should be a line break after this argument."
|
34
|
+
}
|
35
|
+
},
|
36
|
+
|
37
|
+
create(context) {
|
38
|
+
const sourceCode = context.getSourceCode();
|
39
|
+
|
40
|
+
const checkers = {
|
41
|
+
unexpected: {
|
42
|
+
messageId: "unexpectedLineBreak",
|
43
|
+
check: (prevToken, currentToken) => prevToken.loc.start.line !== currentToken.loc.start.line,
|
44
|
+
createFix: (token, tokenBefore) => fixer =>
|
45
|
+
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
|
46
|
+
},
|
47
|
+
missing: {
|
48
|
+
messageId: "missingLineBreak",
|
49
|
+
check: (prevToken, currentToken) => prevToken.loc.start.line === currentToken.loc.start.line,
|
50
|
+
createFix: (token, tokenBefore) => fixer =>
|
51
|
+
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
|
52
|
+
}
|
53
|
+
};
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Check all arguments for line breaks in the CallExpression
|
57
|
+
* @param {CallExpression} node node to evaluate
|
58
|
+
* @param {{ messageId: string, check: Function }} checker selected checker
|
59
|
+
* @returns {void}
|
60
|
+
* @private
|
61
|
+
*/
|
62
|
+
function checkArguments(node, checker) {
|
63
|
+
for (let i = 1; i < node.arguments.length; i++) {
|
64
|
+
const prevArgToken = sourceCode.getFirstToken(node.arguments[i - 1]);
|
65
|
+
const currentArgToken = sourceCode.getFirstToken(node.arguments[i]);
|
66
|
+
|
67
|
+
if (checker.check(prevArgToken, currentArgToken)) {
|
68
|
+
const tokenBefore = sourceCode.getTokenBefore(
|
69
|
+
currentArgToken,
|
70
|
+
{ includeComments: true }
|
71
|
+
);
|
72
|
+
|
73
|
+
context.report({
|
74
|
+
node,
|
75
|
+
loc: {
|
76
|
+
start: tokenBefore.loc.end,
|
77
|
+
end: currentArgToken.loc.start
|
78
|
+
},
|
79
|
+
messageId: checker.messageId,
|
80
|
+
fix: checker.createFix(currentArgToken, tokenBefore)
|
81
|
+
});
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Check if open space is present in a function name
|
88
|
+
* @param {CallExpression} node node to evaluate
|
89
|
+
* @returns {void}
|
90
|
+
* @private
|
91
|
+
*/
|
92
|
+
function check(node) {
|
93
|
+
if (node.arguments.length < 2) {
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
|
97
|
+
const option = context.options[0] || "always";
|
98
|
+
|
99
|
+
if (option === "never") {
|
100
|
+
checkArguments(node, checkers.unexpected);
|
101
|
+
} else if (option === "always") {
|
102
|
+
checkArguments(node, checkers.missing);
|
103
|
+
} else if (option === "consistent") {
|
104
|
+
const firstArgToken = sourceCode.getFirstToken(node.arguments[0]);
|
105
|
+
const secondArgToken = sourceCode.getFirstToken(node.arguments[1]);
|
106
|
+
|
107
|
+
if (firstArgToken.loc.start.line === secondArgToken.loc.start.line) {
|
108
|
+
checkArguments(node, checkers.unexpected);
|
109
|
+
} else {
|
110
|
+
checkArguments(node, checkers.missing);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
return {
|
116
|
+
CallExpression: check,
|
117
|
+
NewExpression: check
|
118
|
+
};
|
119
|
+
}
|
120
|
+
};
|