eslint 4.4.1 → 4.5.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 +13 -0
- package/lib/formatters/junit.js +2 -8
- package/lib/formatters/stylish.js +2 -1
- package/lib/linter.js +1 -1
- package/lib/rules/indent.js +127 -63
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
v4.5.0 - August 18, 2017
|
2
|
+
|
3
|
+
* decdd2c Update: allow arbitrary nodes to be ignored in `indent` (fixes #8594) (#9105) (Teddy Katz)
|
4
|
+
* 79062f3 Update: fix indentation of multiline `new.target` expressions (#9116) (Teddy Katz)
|
5
|
+
* d00e24f Upgrade: `chalk` to 2.x release (#9115) (Stephen Edgar)
|
6
|
+
* 6ef734a Docs: add missing word in processor documentation (#9106) (Teddy Katz)
|
7
|
+
* a4f53ba Fix: Include files with no messages in junit results (#9093) (#9094) (Sean DuBois)
|
8
|
+
* 1d6a9c0 Chore: enable eslint-plugin/test-case-shorthand-strings (#9067) (薛定谔的猫)
|
9
|
+
* f8add8f Fix: don't autofix with linter.verifyAndFix when `fix: false` is used (#9098) (Teddy Katz)
|
10
|
+
* 77bcee4 Docs: update instructions for adding TSC members (#9086) (Teddy Katz)
|
11
|
+
* bd09cd5 Update: avoid requiring NaN spaces of indentation (fixes #9083) (#9085) (Teddy Katz)
|
12
|
+
* c93a853 Chore: Remove extra space in blogpost template (#9088) (Kai Cataldo)
|
13
|
+
|
1
14
|
v4.4.1 - August 7, 2017
|
2
15
|
|
3
16
|
* ec93614 Fix: no-multi-spaces to avoid reporting consecutive tabs (fixes #9079) (#9087) (Teddy Katz)
|
package/lib/formatters/junit.js
CHANGED
@@ -39,10 +39,7 @@ module.exports = function(results) {
|
|
39
39
|
|
40
40
|
const messages = result.messages;
|
41
41
|
|
42
|
-
|
43
|
-
output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
|
44
|
-
}
|
45
|
-
|
42
|
+
output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
|
46
43
|
messages.forEach(message => {
|
47
44
|
const type = message.fatal ? "error" : "failure";
|
48
45
|
|
@@ -57,10 +54,7 @@ module.exports = function(results) {
|
|
57
54
|
output += `</${type}>`;
|
58
55
|
output += "</testcase>\n";
|
59
56
|
});
|
60
|
-
|
61
|
-
if (messages.length) {
|
62
|
-
output += "</testsuite>\n";
|
63
|
-
}
|
57
|
+
output += "</testsuite>\n";
|
64
58
|
|
65
59
|
});
|
66
60
|
|
@@ -5,6 +5,7 @@
|
|
5
5
|
"use strict";
|
6
6
|
|
7
7
|
const chalk = require("chalk"),
|
8
|
+
stripAnsi = require("strip-ansi"),
|
8
9
|
table = require("text-table");
|
9
10
|
|
10
11
|
//------------------------------------------------------------------------------
|
@@ -71,7 +72,7 @@ module.exports = function(results) {
|
|
71
72
|
{
|
72
73
|
align: ["", "r", "l"],
|
73
74
|
stringLength(str) {
|
74
|
-
return
|
75
|
+
return stripAnsi(str).length;
|
75
76
|
}
|
76
77
|
}
|
77
78
|
).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
|
package/lib/linter.js
CHANGED
@@ -1210,7 +1210,7 @@ class Linter extends EventEmitter {
|
|
1210
1210
|
fixed = false,
|
1211
1211
|
passNumber = 0;
|
1212
1212
|
const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
|
1213
|
-
const shouldFix = options && options.fix
|
1213
|
+
const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
|
1214
1214
|
|
1215
1215
|
/**
|
1216
1216
|
* This loop continues until one of the following is true:
|
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
|
|
@@ -914,7 +924,7 @@ module.exports = {
|
|
914
924
|
* @param {ASTNode} node Unknown Node
|
915
925
|
* @returns {void}
|
916
926
|
*/
|
917
|
-
function
|
927
|
+
function ignoreNode(node) {
|
918
928
|
const unknownNodeTokens = new Set(sourceCode.getTokens(node, { includeComments: true }));
|
919
929
|
|
920
930
|
unknownNodeTokens.forEach(token => {
|
@@ -930,19 +940,6 @@ module.exports = {
|
|
930
940
|
});
|
931
941
|
}
|
932
942
|
|
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
943
|
/**
|
947
944
|
* Check whether the given token is the first token of a statement.
|
948
945
|
* @param {Token} token The token to check.
|
@@ -960,7 +957,7 @@ module.exports = {
|
|
960
957
|
return !node || node.range[0] === token.range[0];
|
961
958
|
}
|
962
959
|
|
963
|
-
|
960
|
+
const baseOffsetListeners = {
|
964
961
|
"ArrayExpression, ArrayPattern"(node) {
|
965
962
|
const openingBracket = sourceCode.getFirstToken(node);
|
966
963
|
const closingBracket = sourceCode.getTokenAfter(lodash.findLast(node.elements) || openingBracket, astUtils.isClosingBracketToken);
|
@@ -1178,14 +1175,15 @@ module.exports = {
|
|
1178
1175
|
}
|
1179
1176
|
},
|
1180
1177
|
|
1181
|
-
"MemberExpression, JSXMemberExpression"(node) {
|
1182
|
-
const
|
1178
|
+
"MemberExpression, JSXMemberExpression, MetaProperty"(node) {
|
1179
|
+
const object = node.type === "MetaProperty" ? node.meta : node.object;
|
1180
|
+
const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken);
|
1183
1181
|
const secondNonObjectToken = sourceCode.getTokenAfter(firstNonObjectToken);
|
1184
1182
|
|
1185
|
-
const objectParenCount = sourceCode.getTokensBetween(
|
1183
|
+
const objectParenCount = sourceCode.getTokensBetween(object, node.property, { filter: astUtils.isClosingParenToken }).length;
|
1186
1184
|
const firstObjectToken = objectParenCount
|
1187
|
-
? sourceCode.getTokenBefore(
|
1188
|
-
: sourceCode.getFirstToken(
|
1185
|
+
? sourceCode.getTokenBefore(object, { skip: objectParenCount - 1 })
|
1186
|
+
: sourceCode.getFirstToken(object);
|
1189
1187
|
const lastObjectToken = sourceCode.getTokenBefore(firstNonObjectToken);
|
1190
1188
|
const firstPropertyToken = node.computed ? firstNonObjectToken : secondNonObjectToken;
|
1191
1189
|
|
@@ -1333,8 +1331,6 @@ module.exports = {
|
|
1333
1331
|
}
|
1334
1332
|
},
|
1335
1333
|
|
1336
|
-
"*:exit": checkForUnknownNode,
|
1337
|
-
|
1338
1334
|
"JSXAttribute[value]"(node) {
|
1339
1335
|
const equalsToken = sourceCode.getFirstTokenBetween(node.name, node.value, token => token.type === "Punctuator" && token.value === "=");
|
1340
1336
|
|
@@ -1378,62 +1374,130 @@ module.exports = {
|
|
1378
1374
|
1
|
1379
1375
|
);
|
1380
1376
|
offsets.setDesiredOffset(closingCurly, openingCurly, 0);
|
1381
|
-
}
|
1377
|
+
}
|
1378
|
+
};
|
1382
1379
|
|
1383
|
-
|
1384
|
-
addParensIndent(sourceCode.ast.tokens);
|
1380
|
+
const listenerCallQueue = [];
|
1385
1381
|
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1382
|
+
/*
|
1383
|
+
* To ignore the indentation of a node:
|
1384
|
+
* 1. Don't call the node's listener when entering it (if it has a listener)
|
1385
|
+
* 2. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
|
1386
|
+
*/
|
1387
|
+
const offsetListeners = lodash.mapValues(
|
1388
|
+
baseOffsetListeners,
|
1389
|
+
|
1390
|
+
/*
|
1391
|
+
* Offset listener calls are deferred until traversal is finished, and are called as
|
1392
|
+
* part of the final `Program:exit` listener. This is necessary because a node might
|
1393
|
+
* be matched by multiple selectors.
|
1394
|
+
*
|
1395
|
+
* Example: Suppose there is an offset listener for `Identifier`, and the user has
|
1396
|
+
* specified in configuration that `MemberExpression > Identifier` should be ignored.
|
1397
|
+
* Due to selector specificity rules, the `Identifier` listener will get called first. However,
|
1398
|
+
* if a given Identifier node is supposed to be ignored, then the `Identifier` offset listener
|
1399
|
+
* should not have been called at all. Without doing extra selector matching, we don't know
|
1400
|
+
* whether the Identifier matches the `MemberExpression > Identifier` selector until the
|
1401
|
+
* `MemberExpression > Identifier` listener is called.
|
1402
|
+
*
|
1403
|
+
* To avoid this, the `Identifier` listener isn't called until traversal finishes and all
|
1404
|
+
* ignored nodes are known.
|
1405
|
+
*/
|
1406
|
+
listener =>
|
1407
|
+
node =>
|
1408
|
+
listenerCallQueue.push({ listener, node })
|
1409
|
+
);
|
1392
1410
|
|
1393
|
-
|
1394
|
-
|
1411
|
+
// For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
|
1412
|
+
const ignoredNodes = new Set();
|
1413
|
+
const addToIgnoredNodes = ignoredNodes.add.bind(ignoredNodes);
|
1395
1414
|
|
1396
|
-
|
1397
|
-
|
1415
|
+
const ignoredNodeListeners = options.ignoredNodes.reduce(
|
1416
|
+
(listeners, ignoredSelector) => Object.assign(listeners, { [ignoredSelector]: addToIgnoredNodes }),
|
1417
|
+
{}
|
1418
|
+
);
|
1398
1419
|
|
1399
|
-
|
1420
|
+
/*
|
1421
|
+
* Join the listeners, and add a listener to verify that all tokens actually have the correct indentation
|
1422
|
+
* at the end.
|
1423
|
+
*
|
1424
|
+
* Using Object.assign will cause some offset listeners to be overwritten if the same selector also appears
|
1425
|
+
* in `ignoredNodeListeners`. This isn't a problem because all of the matching nodes will be ignored,
|
1426
|
+
* so those listeners wouldn't be called anyway.
|
1427
|
+
*/
|
1428
|
+
return Object.assign(
|
1429
|
+
offsetListeners,
|
1430
|
+
ignoredNodeListeners,
|
1431
|
+
{
|
1432
|
+
"*:exit"(node) {
|
1400
1433
|
|
1401
|
-
|
1402
|
-
|
1434
|
+
// If a node's type is nonstandard, we can't tell how its children should be offset, so ignore it.
|
1435
|
+
if (!KNOWN_NODES.has(node.type)) {
|
1436
|
+
ignoredNodes.add(node);
|
1403
1437
|
}
|
1438
|
+
},
|
1439
|
+
"Program:exit"() {
|
1404
1440
|
|
1405
|
-
|
1441
|
+
// Invoke the queued offset listeners for the nodes that aren't ignored.
|
1442
|
+
listenerCallQueue
|
1443
|
+
.filter(nodeInfo => !ignoredNodes.has(nodeInfo.node))
|
1444
|
+
.forEach(nodeInfo => nodeInfo.listener(nodeInfo.node));
|
1406
1445
|
|
1407
|
-
|
1446
|
+
// Update the offsets for ignored nodes to prevent their child tokens from being reported.
|
1447
|
+
ignoredNodes.forEach(ignoreNode);
|
1408
1448
|
|
1409
|
-
|
1410
|
-
return;
|
1411
|
-
}
|
1449
|
+
addParensIndent(sourceCode.ast.tokens);
|
1412
1450
|
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1451
|
+
/*
|
1452
|
+
* Create a Map from (tokenOrComment) => (precedingToken).
|
1453
|
+
* This is necessary because sourceCode.getTokenBefore does not handle a comment as an argument correctly.
|
1454
|
+
*/
|
1455
|
+
const precedingTokens = sourceCode.ast.comments.reduce((commentMap, comment) => {
|
1456
|
+
const tokenOrCommentBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
1457
|
+
|
1458
|
+
return commentMap.set(comment, commentMap.has(tokenOrCommentBefore) ? commentMap.get(tokenOrCommentBefore) : tokenOrCommentBefore);
|
1459
|
+
}, new WeakMap());
|
1417
1460
|
|
1418
|
-
|
1419
|
-
const
|
1420
|
-
const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
|
1461
|
+
sourceCode.lines.forEach((line, lineIndex) => {
|
1462
|
+
const lineNumber = lineIndex + 1;
|
1421
1463
|
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
|
1426
|
-
) {
|
1464
|
+
if (!tokenInfo.firstTokensByLineNumber.has(lineNumber)) {
|
1465
|
+
|
1466
|
+
// Don't check indentation on blank lines
|
1427
1467
|
return;
|
1428
1468
|
}
|
1429
|
-
}
|
1430
1469
|
|
1431
|
-
|
1432
|
-
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
|
1433
|
-
});
|
1434
|
-
}
|
1470
|
+
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(lineNumber);
|
1435
1471
|
|
1436
|
-
|
1472
|
+
if (firstTokenOfLine.loc.start.line !== lineNumber) {
|
1473
|
+
|
1474
|
+
// Don't check the indentation of multi-line tokens (e.g. template literals or block comments) twice.
|
1475
|
+
return;
|
1476
|
+
}
|
1477
|
+
|
1478
|
+
// If the token matches the expected expected indentation, don't report it.
|
1479
|
+
if (validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine))) {
|
1480
|
+
return;
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
if (astUtils.isCommentToken(firstTokenOfLine)) {
|
1484
|
+
const tokenBefore = precedingTokens.get(firstTokenOfLine);
|
1485
|
+
const tokenAfter = tokenBefore ? sourceCode.getTokenAfter(tokenBefore) : sourceCode.ast.tokens[0];
|
1437
1486
|
|
1487
|
+
// If a comment matches the expected indentation of the token immediately before or after, don't report it.
|
1488
|
+
if (
|
1489
|
+
tokenBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
|
1490
|
+
tokenAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
|
1491
|
+
) {
|
1492
|
+
return;
|
1493
|
+
}
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
// Otherwise, report the token/comment.
|
1497
|
+
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
|
1498
|
+
});
|
1499
|
+
}
|
1500
|
+
}
|
1501
|
+
);
|
1438
1502
|
}
|
1439
1503
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.5.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -37,7 +37,7 @@
|
|
37
37
|
"dependencies": {
|
38
38
|
"ajv": "^5.2.0",
|
39
39
|
"babel-code-frame": "^6.22.0",
|
40
|
-
"chalk": "^
|
40
|
+
"chalk": "^2.1.0",
|
41
41
|
"concat-stream": "^1.6.0",
|
42
42
|
"cross-spawn": "^5.1.0",
|
43
43
|
"debug": "^2.6.8",
|
@@ -68,6 +68,7 @@
|
|
68
68
|
"progress": "^2.0.0",
|
69
69
|
"require-uncached": "^1.0.3",
|
70
70
|
"semver": "^5.3.0",
|
71
|
+
"strip-ansi": "^4.0.0",
|
71
72
|
"strip-json-comments": "~2.0.1",
|
72
73
|
"table": "^4.0.1",
|
73
74
|
"text-table": "~0.2.0"
|