eslint 5.9.0 → 5.12.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/CHANGELOG.md +55 -0
- package/README.md +2 -2
- package/conf/eslint-recommended.js +1 -0
- package/lib/cli-engine.js +1 -1
- package/lib/config/autoconfig.js +0 -1
- package/lib/config/config-file.js +2 -2
- package/lib/config/config-rule.js +4 -4
- package/lib/config/config-validator.js +2 -2
- package/lib/config.js +15 -2
- package/lib/linter.js +8 -120
- package/lib/rules/array-element-newline.js +0 -1
- package/lib/rules/block-spacing.js +2 -2
- package/lib/rules/camelcase.js +24 -14
- package/lib/rules/capitalized-comments.js +2 -2
- package/lib/rules/comma-style.js +7 -2
- package/lib/rules/eqeqeq.js +0 -1
- package/lib/rules/func-name-matching.js +4 -4
- package/lib/rules/handle-callback-err.js +1 -1
- package/lib/rules/implicit-arrow-linebreak.js +143 -2
- package/lib/rules/indent-legacy.js +0 -3
- package/lib/rules/indent.js +29 -23
- package/lib/rules/keyword-spacing.js +5 -1
- package/lib/rules/max-classes-per-file.js +1 -1
- package/lib/rules/newline-before-return.js +1 -1
- package/lib/rules/no-constant-condition.js +0 -1
- package/lib/rules/no-else-return.js +0 -1
- package/lib/rules/no-implied-eval.js +1 -1
- package/lib/rules/no-irregular-whitespace.js +1 -1
- package/lib/rules/no-param-reassign.js +8 -0
- package/lib/rules/no-restricted-imports.js +1 -1
- package/lib/rules/no-this-before-super.js +0 -1
- package/lib/rules/no-useless-catch.js +52 -0
- package/lib/rules/object-curly-newline.js +0 -1
- package/lib/rules/one-var.js +0 -1
- package/lib/rules/padding-line-between-statements.js +37 -0
- package/lib/rules/prefer-object-spread.js +2 -2
- package/lib/rules/quotes.js +48 -25
- package/lib/rules/require-jsdoc.js +4 -1
- package/lib/rules/sort-imports.js +43 -37
- package/lib/rules/space-in-parens.js +0 -1
- package/lib/rules/space-infix-ops.js +4 -1
- package/lib/rules/valid-jsdoc.js +4 -1
- package/lib/util/config-comment-parser.js +144 -0
- package/lib/util/glob-utils.js +1 -1
- package/lib/{ignored-paths.js → util/ignored-paths.js} +4 -4
- package/lib/{report-translator.js → util/report-translator.js} +2 -2
- package/lib/util/source-code.js +2 -1
- package/messages/all-files-ignored.txt +1 -1
- package/package.json +7 -8
@@ -4,6 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
const {
|
8
|
+
isArrowToken,
|
9
|
+
isParenthesised,
|
10
|
+
isOpeningParenToken
|
11
|
+
} = require("../util/ast-utils");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Rule Definition
|
9
15
|
//------------------------------------------------------------------------------
|
@@ -41,10 +47,145 @@ module.exports = {
|
|
41
47
|
return context.options[0] || "beside";
|
42
48
|
}
|
43
49
|
|
50
|
+
/**
|
51
|
+
* Formats the comments depending on whether it's a line or block comment.
|
52
|
+
* @param {Comment[]} comments The array of comments between the arrow and body
|
53
|
+
* @param {Integer} column The column number of the first token
|
54
|
+
* @returns {string} A string of comment text joined by line breaks
|
55
|
+
*/
|
56
|
+
function formatComments(comments, column) {
|
57
|
+
const whiteSpaces = " ".repeat(column);
|
58
|
+
|
59
|
+
return `${comments.map(comment => {
|
60
|
+
|
61
|
+
if (comment.type === "Line") {
|
62
|
+
return `//${comment.value}`;
|
63
|
+
}
|
64
|
+
|
65
|
+
return `/*${comment.value}*/`;
|
66
|
+
}).join(`\n${whiteSpaces}`)}\n${whiteSpaces}`;
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Finds the first token to prepend comments to depending on the parent type
|
71
|
+
* @param {Node} node The validated node
|
72
|
+
* @returns {Token|Node} The node to prepend comments to
|
73
|
+
*/
|
74
|
+
function findFirstToken(node) {
|
75
|
+
switch (node.parent.type) {
|
76
|
+
case "VariableDeclarator":
|
77
|
+
|
78
|
+
// If the parent is first or only declarator, return the declaration, else, declarator
|
79
|
+
return sourceCode.getFirstToken(
|
80
|
+
node.parent.parent.declarations.length === 1 ||
|
81
|
+
node.parent.parent.declarations[0].id.name === node.parent.id.name
|
82
|
+
? node.parent.parent : node.parent
|
83
|
+
);
|
84
|
+
case "CallExpression":
|
85
|
+
case "Property":
|
86
|
+
|
87
|
+
// find the object key
|
88
|
+
return sourceCode.getFirstToken(node.parent);
|
89
|
+
default:
|
90
|
+
return node;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Helper function for adding parentheses fixes for nodes containing nested arrow functions
|
96
|
+
* @param {Fixer} fixer Fixer
|
97
|
+
* @param {Token} arrow - The arrow token
|
98
|
+
* @param {ASTNode} arrowBody - The arrow function body
|
99
|
+
* @returns {Function[]} autofixer -- wraps function bodies with parentheses
|
100
|
+
*/
|
101
|
+
function addParentheses(fixer, arrow, arrowBody) {
|
102
|
+
const parenthesesFixes = [];
|
103
|
+
let closingParentheses = "";
|
104
|
+
|
105
|
+
let followingBody = arrowBody;
|
106
|
+
let currentArrow = arrow;
|
107
|
+
|
108
|
+
while (currentArrow) {
|
109
|
+
if (!isParenthesised(sourceCode, followingBody)) {
|
110
|
+
parenthesesFixes.push(
|
111
|
+
fixer.insertTextAfter(currentArrow, " (")
|
112
|
+
);
|
113
|
+
|
114
|
+
const paramsToken = sourceCode.getTokenBefore(currentArrow, token =>
|
115
|
+
isOpeningParenToken(token) || token.type === "Identifier");
|
116
|
+
|
117
|
+
const whiteSpaces = " ".repeat(paramsToken.loc.start.column);
|
118
|
+
|
119
|
+
closingParentheses = `\n${whiteSpaces})${closingParentheses}`;
|
120
|
+
}
|
121
|
+
|
122
|
+
currentArrow = sourceCode.getTokenAfter(currentArrow, isArrowToken);
|
123
|
+
|
124
|
+
if (currentArrow) {
|
125
|
+
followingBody = sourceCode.getTokenAfter(currentArrow, token => !isOpeningParenToken(token));
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
return [...parenthesesFixes,
|
130
|
+
fixer.insertTextAfter(arrowBody, closingParentheses)
|
131
|
+
];
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Autofixes the function body to collapse onto the same line as the arrow.
|
136
|
+
* If comments exist, prepends the comments before the arrow function.
|
137
|
+
* If the function body contains arrow functions, appends the function bodies with parentheses.
|
138
|
+
* @param {Token} arrowToken The arrow token.
|
139
|
+
* @param {ASTNode} arrowBody the function body
|
140
|
+
* @param {ASTNode} node The evaluated node
|
141
|
+
* @returns {Function} autofixer -- validates the node to adhere to besides
|
142
|
+
*/
|
143
|
+
function autoFixBesides(arrowToken, arrowBody, node) {
|
144
|
+
return fixer => {
|
145
|
+
const placeBesides = fixer.replaceTextRange([arrowToken.range[1], arrowBody.range[0]], " ");
|
146
|
+
|
147
|
+
const comments = sourceCode.getCommentsInside(node).filter(comment =>
|
148
|
+
comment.loc.start.line < arrowBody.loc.start.line);
|
149
|
+
|
150
|
+
if (comments.length) {
|
151
|
+
|
152
|
+
// If the grandparent is not a variable declarator
|
153
|
+
if (
|
154
|
+
arrowBody.parent &&
|
155
|
+
arrowBody.parent.parent &&
|
156
|
+
arrowBody.parent.parent.type !== "VariableDeclarator"
|
157
|
+
) {
|
158
|
+
|
159
|
+
// If any arrow functions follow, return the necessary parens fixes.
|
160
|
+
if (sourceCode.getTokenAfter(arrowToken, isArrowToken) && arrowBody.parent.parent.type !== "VariableDeclarator") {
|
161
|
+
return addParentheses(fixer, arrowToken, arrowBody);
|
162
|
+
}
|
163
|
+
|
164
|
+
// If any arrow functions precede, the necessary fixes have already been returned, so return null.
|
165
|
+
if (sourceCode.getTokenBefore(arrowToken, isArrowToken) && arrowBody.parent.parent.type !== "VariableDeclarator") {
|
166
|
+
return null;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
const firstToken = findFirstToken(node);
|
171
|
+
|
172
|
+
const commentText = formatComments(comments, firstToken.loc.start.column);
|
173
|
+
|
174
|
+
const commentBeforeExpression = fixer.insertTextBeforeRange(
|
175
|
+
firstToken.range,
|
176
|
+
commentText
|
177
|
+
);
|
178
|
+
|
179
|
+
return [placeBesides, commentBeforeExpression];
|
180
|
+
}
|
181
|
+
|
182
|
+
return placeBesides;
|
183
|
+
};
|
184
|
+
}
|
185
|
+
|
44
186
|
/**
|
45
187
|
* Validates the location of an arrow function body
|
46
188
|
* @param {ASTNode} node The arrow function body
|
47
|
-
* @param {string} keywordName The applicable keyword name for the arrow function body
|
48
189
|
* @returns {void}
|
49
190
|
*/
|
50
191
|
function validateExpression(node) {
|
@@ -76,7 +217,7 @@ module.exports = {
|
|
76
217
|
context.report({
|
77
218
|
node: fixerTarget,
|
78
219
|
message: "Expected no linebreak before this expression.",
|
79
|
-
fix:
|
220
|
+
fix: autoFixBesides(tokenBefore, fixerTarget, node)
|
80
221
|
});
|
81
222
|
}
|
82
223
|
}
|
@@ -300,7 +300,6 @@ module.exports = {
|
|
300
300
|
* @param {int} gottenTabs Indentation tab count in the actual node/code
|
301
301
|
* @param {Object=} loc Error line and column location
|
302
302
|
* @param {boolean} isLastNodeCheck Is the error for last node check
|
303
|
-
* @param {int} lastNodeCheckEndOffset Number of charecters to skip from the end
|
304
303
|
* @returns {void}
|
305
304
|
*/
|
306
305
|
function report(node, needed, gottenSpaces, gottenTabs, loc, isLastNodeCheck) {
|
@@ -365,7 +364,6 @@ module.exports = {
|
|
365
364
|
* Check indent for node
|
366
365
|
* @param {ASTNode} node Node to check
|
367
366
|
* @param {int} neededIndent needed indent
|
368
|
-
* @param {boolean} [excludeCommas=false] skip comma on start of line
|
369
367
|
* @returns {void}
|
370
368
|
*/
|
371
369
|
function checkNodeIndent(node, neededIndent) {
|
@@ -413,7 +411,6 @@ module.exports = {
|
|
413
411
|
* Check indent for nodes list
|
414
412
|
* @param {ASTNode[]} nodes list of node objects
|
415
413
|
* @param {int} indent needed indent
|
416
|
-
* @param {boolean} [excludeCommas=false] skip comma on start of line
|
417
414
|
* @returns {void}
|
418
415
|
*/
|
419
416
|
function checkNodesIndent(nodes, indent) {
|
package/lib/rules/indent.js
CHANGED
@@ -522,25 +522,13 @@ module.exports = {
|
|
522
522
|
},
|
523
523
|
VariableDeclarator: {
|
524
524
|
oneOf: [
|
525
|
-
|
526
|
-
type: "integer",
|
527
|
-
minimum: 0
|
528
|
-
},
|
525
|
+
ELEMENT_LIST_SCHEMA,
|
529
526
|
{
|
530
527
|
type: "object",
|
531
528
|
properties: {
|
532
|
-
var:
|
533
|
-
|
534
|
-
|
535
|
-
},
|
536
|
-
let: {
|
537
|
-
type: "integer",
|
538
|
-
minimum: 0
|
539
|
-
},
|
540
|
-
const: {
|
541
|
-
type: "integer",
|
542
|
-
minimum: 0
|
543
|
-
}
|
529
|
+
var: ELEMENT_LIST_SCHEMA,
|
530
|
+
let: ELEMENT_LIST_SCHEMA,
|
531
|
+
const: ELEMENT_LIST_SCHEMA
|
544
532
|
},
|
545
533
|
additionalProperties: false
|
546
534
|
}
|
@@ -661,7 +649,7 @@ module.exports = {
|
|
661
649
|
if (context.options[1]) {
|
662
650
|
lodash.merge(options, context.options[1]);
|
663
651
|
|
664
|
-
if (typeof options.VariableDeclarator === "number") {
|
652
|
+
if (typeof options.VariableDeclarator === "number" || options.VariableDeclarator === "first") {
|
665
653
|
options.VariableDeclarator = {
|
666
654
|
var: options.VariableDeclarator,
|
667
655
|
let: options.VariableDeclarator,
|
@@ -1229,9 +1217,13 @@ module.exports = {
|
|
1229
1217
|
}
|
1230
1218
|
|
1231
1219
|
const fromToken = sourceCode.getLastToken(node, token => token.type === "Identifier" && token.value === "from");
|
1220
|
+
const sourceToken = sourceCode.getLastToken(node, token => token.type === "String");
|
1221
|
+
const semiToken = sourceCode.getLastToken(node, token => token.type === "Punctuator" && token.value === ";");
|
1232
1222
|
|
1233
1223
|
if (fromToken) {
|
1234
|
-
|
1224
|
+
const end = semiToken && semiToken.range[1] === sourceToken.range[1] ? node.range[1] : sourceToken.range[1];
|
1225
|
+
|
1226
|
+
offsets.setDesiredOffsets([fromToken.range[0], end], sourceCode.getFirstToken(node), 1);
|
1235
1227
|
}
|
1236
1228
|
},
|
1237
1229
|
|
@@ -1345,10 +1337,27 @@ module.exports = {
|
|
1345
1337
|
},
|
1346
1338
|
|
1347
1339
|
VariableDeclaration(node) {
|
1348
|
-
|
1340
|
+
let variableIndent = Object.prototype.hasOwnProperty.call(options.VariableDeclarator, node.kind)
|
1349
1341
|
? options.VariableDeclarator[node.kind]
|
1350
1342
|
: DEFAULT_VARIABLE_INDENT;
|
1351
1343
|
|
1344
|
+
const firstToken = sourceCode.getFirstToken(node),
|
1345
|
+
lastToken = sourceCode.getLastToken(node);
|
1346
|
+
|
1347
|
+
if (options.VariableDeclarator[node.kind] === "first") {
|
1348
|
+
if (node.declarations.length > 1) {
|
1349
|
+
addElementListIndent(
|
1350
|
+
node.declarations,
|
1351
|
+
firstToken,
|
1352
|
+
lastToken,
|
1353
|
+
"first"
|
1354
|
+
);
|
1355
|
+
return;
|
1356
|
+
}
|
1357
|
+
|
1358
|
+
variableIndent = DEFAULT_VARIABLE_INDENT;
|
1359
|
+
}
|
1360
|
+
|
1352
1361
|
if (node.declarations[node.declarations.length - 1].loc.start.line > node.loc.start.line) {
|
1353
1362
|
|
1354
1363
|
/*
|
@@ -1370,13 +1379,10 @@ module.exports = {
|
|
1370
1379
|
* on the same line as the start of the declaration, provided that there are declarators that
|
1371
1380
|
* follow this one.
|
1372
1381
|
*/
|
1373
|
-
const firstToken = sourceCode.getFirstToken(node);
|
1374
|
-
|
1375
1382
|
offsets.setDesiredOffsets(node.range, firstToken, variableIndent, true);
|
1376
1383
|
} else {
|
1377
|
-
offsets.setDesiredOffsets(node.range,
|
1384
|
+
offsets.setDesiredOffsets(node.range, firstToken, variableIndent);
|
1378
1385
|
}
|
1379
|
-
const lastToken = sourceCode.getLastToken(node);
|
1380
1386
|
|
1381
1387
|
if (astUtils.isSemicolonToken(lastToken)) {
|
1382
1388
|
offsets.ignoreToken(lastToken);
|
@@ -453,6 +453,10 @@ module.exports = {
|
|
453
453
|
checkSpacingBefore(firstToken, PREV_TOKEN_M);
|
454
454
|
checkSpacingAfter(firstToken, NEXT_TOKEN_M);
|
455
455
|
|
456
|
+
if (node.type === "ExportDefaultDeclaration") {
|
457
|
+
checkSpacingAround(sourceCode.getTokenAfter(firstToken));
|
458
|
+
}
|
459
|
+
|
456
460
|
if (node.source) {
|
457
461
|
const fromToken = sourceCode.getTokenBefore(node.source);
|
458
462
|
|
@@ -554,7 +558,7 @@ module.exports = {
|
|
554
558
|
// Statements - Declarations
|
555
559
|
ClassDeclaration: checkSpacingForClass,
|
556
560
|
ExportNamedDeclaration: checkSpacingForModuleDeclaration,
|
557
|
-
ExportDefaultDeclaration:
|
561
|
+
ExportDefaultDeclaration: checkSpacingForModuleDeclaration,
|
558
562
|
ExportAllDeclaration: checkSpacingForModuleDeclaration,
|
559
563
|
FunctionDeclaration: checkSpacingForFunction,
|
560
564
|
ImportDeclaration: checkSpacingForModuleDeclaration,
|
@@ -36,7 +36,7 @@ module.exports = {
|
|
36
36
|
/**
|
37
37
|
* Tests whether node is preceded by supplied tokens
|
38
38
|
* @param {ASTNode} node - node to check
|
39
|
-
* @param {
|
39
|
+
* @param {Array} testTokens - array of tokens to test against
|
40
40
|
* @returns {boolean} Whether or not the node is preceded by one of the supplied tokens
|
41
41
|
* @private
|
42
42
|
*/
|
@@ -183,7 +183,6 @@ module.exports = {
|
|
183
183
|
* code paths.
|
184
184
|
*
|
185
185
|
* @param {Node} node The consequent or body node
|
186
|
-
* @param {Node} alternate The alternate node
|
187
186
|
* @returns {boolean} `true` if it is a Return/If node that always returns.
|
188
187
|
*/
|
189
188
|
function checkForReturnOrIf(node) {
|
@@ -38,7 +38,7 @@ module.exports = {
|
|
38
38
|
|
39
39
|
/**
|
40
40
|
* Get the last element of an array, without modifying arr, like pop(), but non-destructive.
|
41
|
-
* @param {
|
41
|
+
* @param {Array} arr What to inspect
|
42
42
|
* @returns {*} The last element of arr
|
43
43
|
* @private
|
44
44
|
*/
|
@@ -30,7 +30,7 @@ module.exports = {
|
|
30
30
|
type: "problem",
|
31
31
|
|
32
32
|
docs: {
|
33
|
-
description: "disallow irregular whitespace
|
33
|
+
description: "disallow irregular whitespace",
|
34
34
|
category: "Possible Errors",
|
35
35
|
recommended: true,
|
36
36
|
url: "https://eslint.org/docs/rules/no-irregular-whitespace"
|
@@ -195,7 +195,7 @@ module.exports = {
|
|
195
195
|
/**
|
196
196
|
* Check if the given importNames are restricted given a list of restrictedImportNames.
|
197
197
|
* @param {Set.<string>} importNames - Set of import names that are being imported
|
198
|
-
* @param {[
|
198
|
+
* @param {string[]} restrictedImportNames - array of import names that are restricted for this import
|
199
199
|
* @returns {boolean} whether the objectName is restricted
|
200
200
|
* @private
|
201
201
|
*/
|
@@ -0,0 +1,52 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Reports useless `catch` clauses that just rethrow their error.
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Rule Definition
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "suggestion",
|
15
|
+
|
16
|
+
docs: {
|
17
|
+
description: "disallow unnecessary `catch` clauses",
|
18
|
+
category: "Best Practices",
|
19
|
+
recommended: false,
|
20
|
+
url: "https://eslint.org/docs/rules/no-useless-catch"
|
21
|
+
},
|
22
|
+
|
23
|
+
schema: []
|
24
|
+
},
|
25
|
+
|
26
|
+
create(context) {
|
27
|
+
return {
|
28
|
+
CatchClause(node) {
|
29
|
+
if (
|
30
|
+
node.param &&
|
31
|
+
node.param.type === "Identifier" &&
|
32
|
+
node.body.body.length &&
|
33
|
+
node.body.body[0].type === "ThrowStatement" &&
|
34
|
+
node.body.body[0].argument.type === "Identifier" &&
|
35
|
+
node.body.body[0].argument.name === node.param.name
|
36
|
+
) {
|
37
|
+
if (node.parent.finalizer) {
|
38
|
+
context.report({
|
39
|
+
node,
|
40
|
+
message: "Unnecessary catch clause."
|
41
|
+
});
|
42
|
+
} else {
|
43
|
+
context.report({
|
44
|
+
node: node.parent,
|
45
|
+
message: "Unnecessary try/catch wrapper."
|
46
|
+
});
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
};
|
51
|
+
}
|
52
|
+
};
|
@@ -172,7 +172,6 @@ module.exports = {
|
|
172
172
|
/**
|
173
173
|
* Reports a given node if it violated this rule.
|
174
174
|
* @param {ASTNode} node - A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node.
|
175
|
-
* @param {{multiline: boolean, minProperties: number, consistent: boolean}} options - An option object.
|
176
175
|
* @returns {void}
|
177
176
|
*/
|
178
177
|
function check(node) {
|
package/lib/rules/one-var.js
CHANGED
@@ -310,7 +310,6 @@ module.exports = {
|
|
310
310
|
/**
|
311
311
|
* Fixer to split a VariableDeclaration into individual declarations
|
312
312
|
* @param {VariableDeclaration} declaration The `VariableDeclaration` to split
|
313
|
-
* @param {?Function} filter Function to filter the declarations
|
314
313
|
* @returns {Function} The fixer function
|
315
314
|
*/
|
316
315
|
function splitDeclarations(declaration) {
|
@@ -36,6 +36,36 @@ function newKeywordTester(keyword) {
|
|
36
36
|
};
|
37
37
|
}
|
38
38
|
|
39
|
+
/**
|
40
|
+
* Creates tester which check if a node starts with specific keyword and spans a single line.
|
41
|
+
*
|
42
|
+
* @param {string} keyword The keyword to test.
|
43
|
+
* @returns {Object} the created tester.
|
44
|
+
* @private
|
45
|
+
*/
|
46
|
+
function newSinglelineKeywordTester(keyword) {
|
47
|
+
return {
|
48
|
+
test: (node, sourceCode) =>
|
49
|
+
node.loc.start.line === node.loc.end.line &&
|
50
|
+
sourceCode.getFirstToken(node).value === keyword
|
51
|
+
};
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Creates tester which check if a node starts with specific keyword and spans multiple lines.
|
56
|
+
*
|
57
|
+
* @param {string} keyword The keyword to test.
|
58
|
+
* @returns {Object} the created tester.
|
59
|
+
* @private
|
60
|
+
*/
|
61
|
+
function newMultilineKeywordTester(keyword) {
|
62
|
+
return {
|
63
|
+
test: (node, sourceCode) =>
|
64
|
+
node.loc.start.line !== node.loc.end.line &&
|
65
|
+
sourceCode.getFirstToken(node).value === keyword
|
66
|
+
};
|
67
|
+
}
|
68
|
+
|
39
69
|
/**
|
40
70
|
* Creates tester which check if a node is specific type.
|
41
71
|
*
|
@@ -368,6 +398,13 @@ const StatementTypes = {
|
|
368
398
|
!isDirectivePrologue(node, sourceCode)
|
369
399
|
},
|
370
400
|
|
401
|
+
"multiline-const": newMultilineKeywordTester("const"),
|
402
|
+
"multiline-let": newMultilineKeywordTester("let"),
|
403
|
+
"multiline-var": newMultilineKeywordTester("var"),
|
404
|
+
"singleline-const": newSinglelineKeywordTester("const"),
|
405
|
+
"singleline-let": newSinglelineKeywordTester("let"),
|
406
|
+
"singleline-var": newSinglelineKeywordTester("var"),
|
407
|
+
|
371
408
|
block: newNodeTypeTester("BlockStatement"),
|
372
409
|
empty: newNodeTypeTester("EmptyStatement"),
|
373
410
|
function: newNodeTypeTester("FunctionDeclaration"),
|
@@ -226,8 +226,8 @@ module.exports = {
|
|
226
226
|
fixable: "code",
|
227
227
|
|
228
228
|
messages: {
|
229
|
-
useSpreadMessage: "Use an object spread instead of `Object.assign` eg: `{ ...foo }
|
230
|
-
useLiteralMessage: "Use an object literal instead of `Object.assign`. eg: `{ foo: bar }
|
229
|
+
useSpreadMessage: "Use an object spread instead of `Object.assign` eg: `{ ...foo }`.",
|
230
|
+
useLiteralMessage: "Use an object literal instead of `Object.assign`. eg: `{ foo: bar }`."
|
231
231
|
}
|
232
232
|
},
|
233
233
|
|
package/lib/rules/quotes.js
CHANGED
@@ -228,6 +228,34 @@ module.exports = {
|
|
228
228
|
}
|
229
229
|
}
|
230
230
|
|
231
|
+
/**
|
232
|
+
* Checks whether or not a given TemplateLiteral node is actually using any of the special features provided by template literal strings.
|
233
|
+
* @param {ASTNode} node - A TemplateLiteral node to check.
|
234
|
+
* @returns {boolean} Whether or not the TemplateLiteral node is using any of the special features provided by template literal strings.
|
235
|
+
* @private
|
236
|
+
*/
|
237
|
+
function isUsingFeatureOfTemplateLiteral(node) {
|
238
|
+
const hasTag = node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi;
|
239
|
+
|
240
|
+
if (hasTag) {
|
241
|
+
return true;
|
242
|
+
}
|
243
|
+
|
244
|
+
const hasStringInterpolation = node.expressions.length > 0;
|
245
|
+
|
246
|
+
if (hasStringInterpolation) {
|
247
|
+
return true;
|
248
|
+
}
|
249
|
+
|
250
|
+
const isMultilineString = node.quasis.length >= 1 && UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw);
|
251
|
+
|
252
|
+
if (isMultilineString) {
|
253
|
+
return true;
|
254
|
+
}
|
255
|
+
|
256
|
+
return false;
|
257
|
+
}
|
258
|
+
|
231
259
|
return {
|
232
260
|
|
233
261
|
Literal(node) {
|
@@ -260,39 +288,34 @@ module.exports = {
|
|
260
288
|
|
261
289
|
TemplateLiteral(node) {
|
262
290
|
|
263
|
-
//
|
291
|
+
// Don't throw an error if backticks are expected or a template literal feature is in use.
|
264
292
|
if (
|
265
293
|
allowTemplateLiterals ||
|
266
294
|
quoteOption === "backtick" ||
|
267
|
-
node
|
295
|
+
isUsingFeatureOfTemplateLiteral(node)
|
268
296
|
) {
|
269
297
|
return;
|
270
298
|
}
|
271
299
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
* in the directive prologue, then fixing them might turn them into directives and change
|
288
|
-
* the behavior of the code.
|
289
|
-
*/
|
290
|
-
return null;
|
291
|
-
}
|
292
|
-
return fixer.replaceText(node, settings.convert(sourceCode.getText(node)));
|
300
|
+
context.report({
|
301
|
+
node,
|
302
|
+
message: "Strings must use {{description}}.",
|
303
|
+
data: {
|
304
|
+
description: settings.description
|
305
|
+
},
|
306
|
+
fix(fixer) {
|
307
|
+
if (isPartOfDirectivePrologue(node)) {
|
308
|
+
|
309
|
+
/*
|
310
|
+
* TemplateLiterals in a directive prologue aren't actually directives, but if they're
|
311
|
+
* in the directive prologue, then fixing them might turn them into directives and change
|
312
|
+
* the behavior of the code.
|
313
|
+
*/
|
314
|
+
return null;
|
293
315
|
}
|
294
|
-
|
295
|
-
|
316
|
+
return fixer.replaceText(node, settings.convert(sourceCode.getText(node)));
|
317
|
+
}
|
318
|
+
});
|
296
319
|
}
|
297
320
|
};
|
298
321
|
|