eslint 4.4.0 → 4.6.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/CHANGELOG.md +56 -0
- package/bin/eslint.js +2 -1
- package/conf/eslint-recommended.js +1 -0
- package/lib/ast-utils.js +11 -17
- package/lib/code-path-analysis/code-path-analyzer.js +8 -4
- package/lib/code-path-analysis/code-path-segment.js +2 -1
- package/lib/code-path-analysis/code-path-state.js +18 -9
- package/lib/code-path-analysis/code-path.js +2 -1
- package/lib/code-path-analysis/fork-context.js +2 -1
- package/lib/config/config-initializer.js +3 -1
- package/lib/config.js +8 -12
- package/lib/formatters/junit.js +2 -8
- package/lib/formatters/stylish.js +2 -1
- package/lib/ignored-paths.js +0 -2
- package/lib/linter.js +320 -318
- package/lib/report-translator.js +274 -0
- package/lib/rules/function-paren-newline.js +221 -0
- package/lib/rules/generator-star-spacing.js +70 -19
- package/lib/rules/indent-legacy.js +2 -1
- package/lib/rules/indent.js +137 -64
- package/lib/rules/no-extra-parens.js +37 -32
- package/lib/rules/no-invalid-this.js +2 -1
- package/lib/rules/no-multi-spaces.js +5 -2
- package/lib/rules/no-unused-vars.js +47 -4
- package/lib/rules/padded-blocks.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +1 -2
- package/lib/testers/rule-tester.js +11 -9
- package/lib/timing.js +2 -2
- package/lib/util/fix-tracker.js +1 -2
- package/lib/util/npm-util.js +21 -4
- package/lib/util/source-code-fixer.js +5 -14
- package/package.json +3 -2
- package/lib/rule-context.js +0 -241
- package/lib/testers/event-generator-tester.js +0 -62
- package/lib/testers/test-parser.js +0 -48
package/lib/rules/indent.js
CHANGED
@@ -581,6 +581,15 @@ module.exports = {
|
|
581
581
|
ImportDeclaration: ELEMENT_LIST_SCHEMA,
|
582
582
|
flatTernaryExpressions: {
|
583
583
|
type: "boolean"
|
584
|
+
},
|
585
|
+
ignoredNodes: {
|
586
|
+
type: "array",
|
587
|
+
items: {
|
588
|
+
type: "string",
|
589
|
+
not: {
|
590
|
+
pattern: ":exit$"
|
591
|
+
}
|
592
|
+
}
|
584
593
|
}
|
585
594
|
},
|
586
595
|
additionalProperties: false
|
@@ -618,7 +627,8 @@ module.exports = {
|
|
618
627
|
ArrayExpression: 1,
|
619
628
|
ObjectExpression: 1,
|
620
629
|
ImportDeclaration: 1,
|
621
|
-
flatTernaryExpressions: false
|
630
|
+
flatTernaryExpressions: false,
|
631
|
+
ignoredNodes: []
|
622
632
|
};
|
623
633
|
|
624
634
|
if (context.options.length) {
|
@@ -783,7 +793,7 @@ module.exports = {
|
|
783
793
|
offsets.setDesiredOffsets(
|
784
794
|
[startToken.range[1], endToken.range[0]],
|
785
795
|
startToken,
|
786
|
-
offset === "
|
796
|
+
typeof offset === "number" ? offset : 1
|
787
797
|
);
|
788
798
|
offsets.setDesiredOffset(endToken, startToken, 0);
|
789
799
|
|
@@ -792,10 +802,19 @@ module.exports = {
|
|
792
802
|
return;
|
793
803
|
}
|
794
804
|
elements.forEach((element, index) => {
|
805
|
+
if (!element) {
|
806
|
+
|
807
|
+
// Skip holes in arrays
|
808
|
+
return;
|
809
|
+
}
|
795
810
|
if (offset === "off") {
|
811
|
+
|
812
|
+
// Ignore the first token of every element if the "off" option is used
|
796
813
|
offsets.ignoreToken(getFirstToken(element));
|
797
814
|
}
|
798
|
-
|
815
|
+
|
816
|
+
// Offset the following elements correctly relative to the first element
|
817
|
+
if (index === 0) {
|
799
818
|
return;
|
800
819
|
}
|
801
820
|
if (offset === "first" && tokenInfo.isFirstTokenOfLine(getFirstToken(element))) {
|
@@ -914,7 +933,7 @@ module.exports = {
|
|
914
933
|
* @param {ASTNode} node Unknown Node
|
915
934
|
* @returns {void}
|
916
935
|
*/
|
917
|
-
function
|
936
|
+
function ignoreNode(node) {
|
918
937
|
const unknownNodeTokens = new Set(sourceCode.getTokens(node, { includeComments: true }));
|
919
938
|
|
920
939
|
unknownNodeTokens.forEach(token => {
|
@@ -930,19 +949,6 @@ module.exports = {
|
|
930
949
|
});
|
931
950
|
}
|
932
951
|
|
933
|
-
/**
|
934
|
-
* Ignore node if it is unknown
|
935
|
-
* @param {ASTNode} node Node
|
936
|
-
* @returns {void}
|
937
|
-
*/
|
938
|
-
function checkForUnknownNode(node) {
|
939
|
-
const isNodeUnknown = !(KNOWN_NODES.has(node.type));
|
940
|
-
|
941
|
-
if (isNodeUnknown) {
|
942
|
-
ignoreUnknownNode(node);
|
943
|
-
}
|
944
|
-
}
|
945
|
-
|
946
952
|
/**
|
947
953
|
* Check whether the given token is the first token of a statement.
|
948
954
|
* @param {Token} token The token to check.
|
@@ -960,7 +966,7 @@ module.exports = {
|
|
960
966
|
return !node || node.range[0] === token.range[0];
|
961
967
|
}
|
962
968
|
|
963
|
-
|
969
|
+
const baseOffsetListeners = {
|
964
970
|
"ArrayExpression, ArrayPattern"(node) {
|
965
971
|
const openingBracket = sourceCode.getFirstToken(node);
|
966
972
|
const closingBracket = sourceCode.getTokenAfter(lodash.findLast(node.elements) || openingBracket, astUtils.isClosingBracketToken);
|
@@ -1178,14 +1184,15 @@ module.exports = {
|
|
1178
1184
|
}
|
1179
1185
|
},
|
1180
1186
|
|
1181
|
-
"MemberExpression, JSXMemberExpression"(node) {
|
1182
|
-
const
|
1187
|
+
"MemberExpression, JSXMemberExpression, MetaProperty"(node) {
|
1188
|
+
const object = node.type === "MetaProperty" ? node.meta : node.object;
|
1189
|
+
const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken);
|
1183
1190
|
const secondNonObjectToken = sourceCode.getTokenAfter(firstNonObjectToken);
|
1184
1191
|
|
1185
|
-
const objectParenCount = sourceCode.getTokensBetween(
|
1192
|
+
const objectParenCount = sourceCode.getTokensBetween(object, node.property, { filter: astUtils.isClosingParenToken }).length;
|
1186
1193
|
const firstObjectToken = objectParenCount
|
1187
|
-
? sourceCode.getTokenBefore(
|
1188
|
-
: sourceCode.getFirstToken(
|
1194
|
+
? sourceCode.getTokenBefore(object, { skip: objectParenCount - 1 })
|
1195
|
+
: sourceCode.getFirstToken(object);
|
1189
1196
|
const lastObjectToken = sourceCode.getTokenBefore(firstNonObjectToken);
|
1190
1197
|
const firstPropertyToken = node.computed ? firstNonObjectToken : secondNonObjectToken;
|
1191
1198
|
|
@@ -1333,8 +1340,6 @@ module.exports = {
|
|
1333
1340
|
}
|
1334
1341
|
},
|
1335
1342
|
|
1336
|
-
"*:exit": checkForUnknownNode,
|
1337
|
-
|
1338
1343
|
"JSXAttribute[value]"(node) {
|
1339
1344
|
const equalsToken = sourceCode.getFirstTokenBetween(node.name, node.value, token => token.type === "Punctuator" && token.value === "=");
|
1340
1345
|
|
@@ -1378,62 +1383,130 @@ module.exports = {
|
|
1378
1383
|
1
|
1379
1384
|
);
|
1380
1385
|
offsets.setDesiredOffset(closingCurly, openingCurly, 0);
|
1381
|
-
}
|
1386
|
+
}
|
1387
|
+
};
|
1382
1388
|
|
1383
|
-
|
1384
|
-
addParensIndent(sourceCode.ast.tokens);
|
1389
|
+
const listenerCallQueue = [];
|
1385
1390
|
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1391
|
+
/*
|
1392
|
+
* To ignore the indentation of a node:
|
1393
|
+
* 1. Don't call the node's listener when entering it (if it has a listener)
|
1394
|
+
* 2. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
|
1395
|
+
*/
|
1396
|
+
const offsetListeners = lodash.mapValues(
|
1397
|
+
baseOffsetListeners,
|
1398
|
+
|
1399
|
+
/*
|
1400
|
+
* Offset listener calls are deferred until traversal is finished, and are called as
|
1401
|
+
* part of the final `Program:exit` listener. This is necessary because a node might
|
1402
|
+
* be matched by multiple selectors.
|
1403
|
+
*
|
1404
|
+
* Example: Suppose there is an offset listener for `Identifier`, and the user has
|
1405
|
+
* specified in configuration that `MemberExpression > Identifier` should be ignored.
|
1406
|
+
* Due to selector specificity rules, the `Identifier` listener will get called first. However,
|
1407
|
+
* if a given Identifier node is supposed to be ignored, then the `Identifier` offset listener
|
1408
|
+
* should not have been called at all. Without doing extra selector matching, we don't know
|
1409
|
+
* whether the Identifier matches the `MemberExpression > Identifier` selector until the
|
1410
|
+
* `MemberExpression > Identifier` listener is called.
|
1411
|
+
*
|
1412
|
+
* To avoid this, the `Identifier` listener isn't called until traversal finishes and all
|
1413
|
+
* ignored nodes are known.
|
1414
|
+
*/
|
1415
|
+
listener =>
|
1416
|
+
node =>
|
1417
|
+
listenerCallQueue.push({ listener, node })
|
1418
|
+
);
|
1392
1419
|
|
1393
|
-
|
1394
|
-
|
1420
|
+
// For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
|
1421
|
+
const ignoredNodes = new Set();
|
1422
|
+
const addToIgnoredNodes = ignoredNodes.add.bind(ignoredNodes);
|
1395
1423
|
|
1396
|
-
|
1397
|
-
|
1424
|
+
const ignoredNodeListeners = options.ignoredNodes.reduce(
|
1425
|
+
(listeners, ignoredSelector) => Object.assign(listeners, { [ignoredSelector]: addToIgnoredNodes }),
|
1426
|
+
{}
|
1427
|
+
);
|
1398
1428
|
|
1399
|
-
|
1429
|
+
/*
|
1430
|
+
* Join the listeners, and add a listener to verify that all tokens actually have the correct indentation
|
1431
|
+
* at the end.
|
1432
|
+
*
|
1433
|
+
* Using Object.assign will cause some offset listeners to be overwritten if the same selector also appears
|
1434
|
+
* in `ignoredNodeListeners`. This isn't a problem because all of the matching nodes will be ignored,
|
1435
|
+
* so those listeners wouldn't be called anyway.
|
1436
|
+
*/
|
1437
|
+
return Object.assign(
|
1438
|
+
offsetListeners,
|
1439
|
+
ignoredNodeListeners,
|
1440
|
+
{
|
1441
|
+
"*:exit"(node) {
|
1400
1442
|
|
1401
|
-
|
1402
|
-
|
1443
|
+
// If a node's type is nonstandard, we can't tell how its children should be offset, so ignore it.
|
1444
|
+
if (!KNOWN_NODES.has(node.type)) {
|
1445
|
+
ignoredNodes.add(node);
|
1403
1446
|
}
|
1447
|
+
},
|
1448
|
+
"Program:exit"() {
|
1404
1449
|
|
1405
|
-
|
1450
|
+
// Invoke the queued offset listeners for the nodes that aren't ignored.
|
1451
|
+
listenerCallQueue
|
1452
|
+
.filter(nodeInfo => !ignoredNodes.has(nodeInfo.node))
|
1453
|
+
.forEach(nodeInfo => nodeInfo.listener(nodeInfo.node));
|
1406
1454
|
|
1407
|
-
|
1455
|
+
// Update the offsets for ignored nodes to prevent their child tokens from being reported.
|
1456
|
+
ignoredNodes.forEach(ignoreNode);
|
1408
1457
|
|
1409
|
-
|
1410
|
-
return;
|
1411
|
-
}
|
1458
|
+
addParensIndent(sourceCode.ast.tokens);
|
1412
1459
|
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1460
|
+
/*
|
1461
|
+
* Create a Map from (tokenOrComment) => (precedingToken).
|
1462
|
+
* This is necessary because sourceCode.getTokenBefore does not handle a comment as an argument correctly.
|
1463
|
+
*/
|
1464
|
+
const precedingTokens = sourceCode.ast.comments.reduce((commentMap, comment) => {
|
1465
|
+
const tokenOrCommentBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
1466
|
+
|
1467
|
+
return commentMap.set(comment, commentMap.has(tokenOrCommentBefore) ? commentMap.get(tokenOrCommentBefore) : tokenOrCommentBefore);
|
1468
|
+
}, new WeakMap());
|
1417
1469
|
|
1418
|
-
|
1419
|
-
const
|
1420
|
-
const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
|
1470
|
+
sourceCode.lines.forEach((line, lineIndex) => {
|
1471
|
+
const lineNumber = lineIndex + 1;
|
1421
1472
|
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
|
1426
|
-
) {
|
1473
|
+
if (!tokenInfo.firstTokensByLineNumber.has(lineNumber)) {
|
1474
|
+
|
1475
|
+
// Don't check indentation on blank lines
|
1427
1476
|
return;
|
1428
1477
|
}
|
1429
|
-
}
|
1430
1478
|
|
1431
|
-
|
1432
|
-
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
|
1433
|
-
});
|
1434
|
-
}
|
1479
|
+
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(lineNumber);
|
1435
1480
|
|
1436
|
-
|
1481
|
+
if (firstTokenOfLine.loc.start.line !== lineNumber) {
|
1437
1482
|
|
1483
|
+
// Don't check the indentation of multi-line tokens (e.g. template literals or block comments) twice.
|
1484
|
+
return;
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
// If the token matches the expected expected indentation, don't report it.
|
1488
|
+
if (validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine))) {
|
1489
|
+
return;
|
1490
|
+
}
|
1491
|
+
|
1492
|
+
if (astUtils.isCommentToken(firstTokenOfLine)) {
|
1493
|
+
const tokenBefore = precedingTokens.get(firstTokenOfLine);
|
1494
|
+
const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
|
1495
|
+
|
1496
|
+
// If a comment matches the expected indentation of the token immediately before or after, don't report it.
|
1497
|
+
if (
|
1498
|
+
tokenBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
|
1499
|
+
tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
|
1500
|
+
) {
|
1501
|
+
return;
|
1502
|
+
}
|
1503
|
+
}
|
1504
|
+
|
1505
|
+
// Otherwise, report the token/comment.
|
1506
|
+
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
|
1507
|
+
});
|
1508
|
+
}
|
1509
|
+
}
|
1510
|
+
);
|
1438
1511
|
}
|
1439
1512
|
};
|
@@ -276,6 +276,15 @@ module.exports = {
|
|
276
276
|
!astUtils.canTokensBeAdjacent(tokenBeforeRightParen, tokenAfterRightParen);
|
277
277
|
}
|
278
278
|
|
279
|
+
/**
|
280
|
+
* Determines if a given expression node is an IIFE
|
281
|
+
* @param {ASTNode} node The node to check
|
282
|
+
* @returns {boolean} `true` if the given node is an IIFE
|
283
|
+
*/
|
284
|
+
function isIIFE(node) {
|
285
|
+
return node.type === "CallExpression" && node.callee.type === "FunctionExpression";
|
286
|
+
}
|
287
|
+
|
279
288
|
/**
|
280
289
|
* Report the node
|
281
290
|
* @param {ASTNode} node node to evaluate
|
@@ -286,8 +295,14 @@ module.exports = {
|
|
286
295
|
const leftParenToken = sourceCode.getTokenBefore(node);
|
287
296
|
const rightParenToken = sourceCode.getTokenAfter(node);
|
288
297
|
|
289
|
-
if (
|
290
|
-
|
298
|
+
if (!isParenthesisedTwice(node)) {
|
299
|
+
if (tokensToIgnore.has(sourceCode.getFirstToken(node))) {
|
300
|
+
return;
|
301
|
+
}
|
302
|
+
|
303
|
+
if (isIIFE(node) && !isParenthesised(node.callee)) {
|
304
|
+
return;
|
305
|
+
}
|
291
306
|
}
|
292
307
|
|
293
308
|
context.report({
|
@@ -328,26 +343,21 @@ module.exports = {
|
|
328
343
|
* @private
|
329
344
|
*/
|
330
345
|
function checkCallNew(node) {
|
331
|
-
if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node)
|
332
|
-
node.type === "
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
!hasDoubleExcessParens(node.callee)
|
338
|
-
)) {
|
339
|
-
report(node.callee);
|
346
|
+
if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node)) {
|
347
|
+
const hasNewParensException = node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee);
|
348
|
+
|
349
|
+
if (hasDoubleExcessParens(node.callee) || !isIIFE(node) && !hasNewParensException) {
|
350
|
+
report(node.callee);
|
351
|
+
}
|
340
352
|
}
|
341
353
|
if (node.arguments.length === 1) {
|
342
354
|
if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
|
343
355
|
report(node.arguments[0]);
|
344
356
|
}
|
345
357
|
} else {
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
}
|
350
|
-
});
|
358
|
+
node.arguments
|
359
|
+
.filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
|
360
|
+
.forEach(report);
|
351
361
|
}
|
352
362
|
}
|
353
363
|
|
@@ -442,11 +452,9 @@ module.exports = {
|
|
442
452
|
|
443
453
|
return {
|
444
454
|
ArrayExpression(node) {
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
}
|
449
|
-
});
|
455
|
+
node.elements
|
456
|
+
.filter(e => e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR)
|
457
|
+
.forEach(report);
|
450
458
|
},
|
451
459
|
|
452
460
|
ArrowFunctionExpression(node) {
|
@@ -589,13 +597,12 @@ module.exports = {
|
|
589
597
|
NewExpression: checkCallNew,
|
590
598
|
|
591
599
|
ObjectExpression(node) {
|
592
|
-
|
593
|
-
|
600
|
+
node.properties
|
601
|
+
.filter(property => {
|
602
|
+
const value = property.value;
|
594
603
|
|
595
|
-
|
596
|
-
|
597
|
-
}
|
598
|
-
});
|
604
|
+
return value && hasExcessParens(value) && precedence(value) >= PRECEDENCE_OF_ASSIGNMENT_EXPR;
|
605
|
+
}).forEach(property => report(property.value));
|
599
606
|
},
|
600
607
|
|
601
608
|
ReturnStatement(node) {
|
@@ -615,11 +622,9 @@ module.exports = {
|
|
615
622
|
},
|
616
623
|
|
617
624
|
SequenceExpression(node) {
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
}
|
622
|
-
});
|
625
|
+
node.expressions
|
626
|
+
.filter(e => hasExcessParens(e) && precedence(e) >= precedence(node))
|
627
|
+
.forEach(report);
|
623
628
|
},
|
624
629
|
|
625
630
|
SwitchCase(node) {
|
@@ -76,8 +76,11 @@ module.exports = {
|
|
76
76
|
}
|
77
77
|
const rightToken = tokensAndComments[leftIndex + 1];
|
78
78
|
|
79
|
-
// Ignore tokens that have
|
80
|
-
if (
|
79
|
+
// Ignore tokens that don't have 2 spaces between them or are on different lines
|
80
|
+
if (
|
81
|
+
!sourceCode.text.slice(leftToken.range[1], rightToken.range[0]).includes(" ") ||
|
82
|
+
leftToken.loc.end.line < rightToken.loc.start.line
|
83
|
+
) {
|
81
84
|
return;
|
82
85
|
}
|
83
86
|
|
@@ -64,8 +64,6 @@ module.exports = {
|
|
64
64
|
create(context) {
|
65
65
|
const sourceCode = context.getSourceCode();
|
66
66
|
|
67
|
-
const DEFINED_MESSAGE = "'{{name}}' is defined but never used.";
|
68
|
-
const ASSIGNED_MESSAGE = "'{{name}}' is assigned a value but never used.";
|
69
67
|
const REST_PROPERTY_TYPE = /^(?:Experimental)?RestProperty$/;
|
70
68
|
|
71
69
|
const config = {
|
@@ -100,6 +98,49 @@ module.exports = {
|
|
100
98
|
}
|
101
99
|
}
|
102
100
|
|
101
|
+
/**
|
102
|
+
* Generate the warning message about the variable being
|
103
|
+
* defined and unused, including the ignore pattern if configured.
|
104
|
+
* @param {Variable} unusedVar - eslint-scope variable object.
|
105
|
+
* @returns {string} The warning message to be used with this unused variable.
|
106
|
+
*/
|
107
|
+
function getDefinedMessage(unusedVar) {
|
108
|
+
let type;
|
109
|
+
let pattern;
|
110
|
+
|
111
|
+
if (config.varsIgnorePattern) {
|
112
|
+
type = "vars";
|
113
|
+
pattern = config.varsIgnorePattern.toString();
|
114
|
+
}
|
115
|
+
|
116
|
+
if (unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type) {
|
117
|
+
const defType = unusedVar.defs[0].type;
|
118
|
+
|
119
|
+
if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
|
120
|
+
type = "args";
|
121
|
+
pattern = config.caughtErrorsIgnorePattern.toString();
|
122
|
+
} else if (defType === "Parameter" && config.argsIgnorePattern) {
|
123
|
+
type = "args";
|
124
|
+
pattern = config.argsIgnorePattern.toString();
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
const additional = type ? ` Allowed unused ${type} must match ${pattern}.` : "";
|
129
|
+
|
130
|
+
return `'{{name}}' is defined but never used.${additional}`;
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Generate the warning message about the variable being
|
135
|
+
* assigned and unused, including the ignore pattern if configured.
|
136
|
+
* @returns {string} The warning message to be used with this unused variable.
|
137
|
+
*/
|
138
|
+
function getAssignedMessage() {
|
139
|
+
const additional = config.varsIgnorePattern ? ` Allowed unused vars must match ${config.varsIgnorePattern.toString()}.` : "";
|
140
|
+
|
141
|
+
return `'{{name}}' is assigned a value but never used.${additional}`;
|
142
|
+
}
|
143
|
+
|
103
144
|
//--------------------------------------------------------------------------
|
104
145
|
// Helpers
|
105
146
|
//--------------------------------------------------------------------------
|
@@ -586,13 +627,15 @@ module.exports = {
|
|
586
627
|
context.report({
|
587
628
|
node: programNode,
|
588
629
|
loc: getLocation(unusedVar),
|
589
|
-
message:
|
630
|
+
message: getDefinedMessage(unusedVar),
|
590
631
|
data: unusedVar
|
591
632
|
});
|
592
633
|
} else if (unusedVar.defs.length > 0) {
|
593
634
|
context.report({
|
594
635
|
node: unusedVar.identifiers[0],
|
595
|
-
message: unusedVar.references.some(ref => ref.isWrite())
|
636
|
+
message: unusedVar.references.some(ref => ref.isWrite())
|
637
|
+
? getAssignedMessage()
|
638
|
+
: getDefinedMessage(unusedVar),
|
596
639
|
data: unusedVar
|
597
640
|
});
|
598
641
|
}
|
@@ -111,7 +111,7 @@ module.exports = {
|
|
111
111
|
* @returns {boolean} Whether or not the token is followed by a blank line.
|
112
112
|
*/
|
113
113
|
function getFirstBlockToken(token) {
|
114
|
-
let prev
|
114
|
+
let prev,
|
115
115
|
first = token;
|
116
116
|
|
117
117
|
do {
|
@@ -129,7 +129,7 @@ module.exports = {
|
|
129
129
|
*/
|
130
130
|
function getLastBlockToken(token) {
|
131
131
|
let last = token,
|
132
|
-
next
|
132
|
+
next;
|
133
133
|
|
134
134
|
do {
|
135
135
|
next = last;
|
@@ -88,7 +88,6 @@ function getCallbackInfo(node) {
|
|
88
88
|
parent.parent.arguments.length === 1 &&
|
89
89
|
parent.parent.arguments[0].type === "ThisExpression"
|
90
90
|
);
|
91
|
-
node = parent;
|
92
91
|
parent = parent.parent;
|
93
92
|
} else {
|
94
93
|
return retv;
|
@@ -133,7 +132,7 @@ function hasDuplicateParams(paramsList) {
|
|
133
132
|
module.exports = {
|
134
133
|
meta: {
|
135
134
|
docs: {
|
136
|
-
description: "require arrow functions
|
135
|
+
description: "require using arrow functions for callbacks",
|
137
136
|
category: "ECMAScript 6",
|
138
137
|
recommended: false
|
139
138
|
},
|
@@ -159,7 +159,8 @@ class RuleTester {
|
|
159
159
|
|
160
160
|
// we have to clone because merge uses the first argument for recipient
|
161
161
|
lodash.cloneDeep(defaultConfig),
|
162
|
-
testerConfig
|
162
|
+
testerConfig,
|
163
|
+
{ rules: { "rule-tester/validate-ast": "error" } }
|
163
164
|
);
|
164
165
|
|
165
166
|
/**
|
@@ -333,13 +334,14 @@ class RuleTester {
|
|
333
334
|
*/
|
334
335
|
linter.reset();
|
335
336
|
|
336
|
-
linter.
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
337
|
+
linter.defineRule("rule-tester/validate-ast", () => ({
|
338
|
+
Program(node) {
|
339
|
+
beforeAST = cloneDeeplyExcludesParent(node);
|
340
|
+
},
|
341
|
+
"Program:exit"(node) {
|
342
|
+
afterAST = node;
|
343
|
+
}
|
344
|
+
}));
|
343
345
|
|
344
346
|
// Freezes rule-context properties.
|
345
347
|
const originalGet = linter.rules.get;
|
@@ -519,7 +521,7 @@ class RuleTester {
|
|
519
521
|
"Expected no autofixes to be suggested"
|
520
522
|
);
|
521
523
|
} else {
|
522
|
-
const fixResult = SourceCodeFixer.applyFixes(
|
524
|
+
const fixResult = SourceCodeFixer.applyFixes(item.code, messages);
|
523
525
|
|
524
526
|
assert.equal(fixResult.output, item.output, "Output is incorrect.");
|
525
527
|
}
|
package/lib/timing.js
CHANGED
@@ -84,11 +84,11 @@ function display(data) {
|
|
84
84
|
}
|
85
85
|
});
|
86
86
|
|
87
|
-
const table = rows.map(row =>
|
87
|
+
const table = rows.map(row => (
|
88
88
|
row
|
89
89
|
.map((cell, index) => ALIGN[index](cell, widths[index]))
|
90
90
|
.join(" | ")
|
91
|
-
);
|
91
|
+
));
|
92
92
|
|
93
93
|
table.splice(1, 0, widths.map((w, index) => {
|
94
94
|
if (index !== 0 && index !== widths.length - 1) {
|
package/lib/util/fix-tracker.js
CHANGED
@@ -57,8 +57,7 @@ class FixTracker {
|
|
57
57
|
retainEnclosingFunction(node) {
|
58
58
|
const functionNode = astUtils.getUpperFunction(node);
|
59
59
|
|
60
|
-
return this.retainRange(
|
61
|
-
functionNode ? functionNode.range : this.sourceCode.ast.range);
|
60
|
+
return this.retainRange(functionNode ? functionNode.range : this.sourceCode.ast.range);
|
62
61
|
}
|
63
62
|
|
64
63
|
/**
|
package/lib/util/npm-util.js
CHANGED
@@ -53,22 +53,39 @@ function installSyncSaveDev(packages) {
|
|
53
53
|
if (!Array.isArray(packages)) {
|
54
54
|
packages = [packages];
|
55
55
|
}
|
56
|
-
spawn.sync("npm", ["i", "--save-dev"].concat(packages),
|
56
|
+
const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packages),
|
57
|
+
{ stdio: "inherit" });
|
58
|
+
const error = npmProcess.error;
|
59
|
+
|
60
|
+
if (error && error.code === "ENOENT") {
|
61
|
+
const pluralS = packages.length > 1 ? "s" : "";
|
62
|
+
|
63
|
+
log.error(`Could not execute npm. Please install the following package${pluralS} with your package manager of choice: ${packages.join(", ")}`);
|
64
|
+
}
|
57
65
|
}
|
58
66
|
|
59
67
|
/**
|
60
68
|
* Fetch `peerDependencies` of the given package by `npm show` command.
|
61
69
|
* @param {string} packageName The package name to fetch peerDependencies.
|
62
|
-
* @returns {Object} Gotten peerDependencies.
|
70
|
+
* @returns {Object} Gotten peerDependencies. Returns null if npm was not found.
|
63
71
|
*/
|
64
72
|
function fetchPeerDependencies(packageName) {
|
65
|
-
const
|
73
|
+
const npmProcess = spawn.sync(
|
66
74
|
"npm",
|
67
75
|
["show", "--json", packageName, "peerDependencies"],
|
68
76
|
{ encoding: "utf8" }
|
69
|
-
)
|
77
|
+
);
|
78
|
+
|
79
|
+
const error = npmProcess.error;
|
80
|
+
|
81
|
+
if (error && error.code === "ENOENT") {
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
const fetchedText = npmProcess.stdout.trim();
|
70
85
|
|
71
86
|
return JSON.parse(fetchedText || "{}");
|
87
|
+
|
88
|
+
|
72
89
|
}
|
73
90
|
|
74
91
|
/**
|