eslint 4.18.0 → 4.19.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 +40 -0
- package/README.md +2 -2
- package/conf/environments.js +3 -1
- package/conf/eslint-recommended.js +0 -0
- package/lib/ast-utils.js +25 -29
- package/lib/cli-engine.js +29 -28
- package/lib/code-path-analysis/code-path-state.js +5 -5
- package/lib/code-path-analysis/code-path.js +11 -6
- package/lib/code-path-analysis/fork-context.js +10 -12
- package/lib/config/config-file.js +20 -11
- package/lib/config/config-ops.js +8 -10
- package/lib/config/config-rule.js +2 -3
- package/lib/config/plugins.js +20 -0
- package/lib/config.js +7 -8
- package/lib/file-finder.js +9 -10
- package/lib/ignored-paths.js +4 -4
- package/lib/linter.js +397 -406
- package/lib/load-rules.js +6 -7
- package/lib/rules/accessor-pairs.js +4 -4
- package/lib/rules/array-callback-return.js +8 -6
- package/lib/rules/array-element-newline.js +4 -4
- package/lib/rules/curly.js +11 -10
- package/lib/rules/generator-star-spacing.js +1 -2
- package/lib/rules/indent-legacy.js +7 -10
- package/lib/rules/indent.js +51 -29
- package/lib/rules/keyword-spacing.js +6 -18
- package/lib/rules/max-len.js +12 -5
- package/lib/rules/no-await-in-loop.js +1 -1
- package/lib/rules/no-buffer-constructor.js +1 -1
- package/lib/rules/no-control-regex.js +51 -72
- package/lib/rules/no-else-return.js +7 -6
- package/lib/rules/no-empty-character-class.js +1 -1
- package/lib/rules/no-eval.js +7 -8
- package/lib/rules/no-extra-parens.js +5 -4
- package/lib/rules/no-implicit-coercion.js +6 -9
- package/lib/rules/no-invalid-regexp.js +53 -36
- package/lib/rules/no-irregular-whitespace.js +1 -1
- package/lib/rules/no-loop-func.js +9 -11
- package/lib/rules/no-magic-numbers.js +17 -10
- package/lib/rules/no-return-assign.js +4 -3
- package/lib/rules/no-unexpected-multiline.js +1 -1
- package/lib/rules/no-unsafe-finally.js +7 -4
- package/lib/rules/no-useless-escape.js +2 -2
- package/lib/rules/no-useless-return.js +10 -9
- package/lib/rules/no-var.js +8 -8
- package/lib/rules/object-curly-newline.js +2 -1
- package/lib/rules/one-var.js +140 -97
- package/lib/rules/padding-line-between-statements.js +6 -4
- package/lib/rules/prefer-arrow-callback.js +5 -4
- package/lib/rules/prefer-template.js +5 -3
- package/lib/rules/space-unary-ops.js +1 -3
- package/lib/rules/spaced-comment.js +3 -7
- package/lib/rules/template-tag-spacing.js +0 -0
- package/lib/rules/valid-jsdoc.js +6 -6
- package/lib/rules/vars-on-top.js +7 -17
- package/lib/timing.js +3 -5
- package/lib/util/glob-util.js +11 -11
- package/lib/util/interpolate.js +5 -1
- package/lib/util/naming.js +11 -10
- package/lib/util/npm-util.js +4 -6
- package/lib/util/path-util.js +6 -8
- package/lib/util/source-code-util.js +23 -26
- package/lib/util/source-code.js +4 -3
- package/package.json +4 -3
- package/conf/default-config-options.js +0 -29
package/lib/load-rules.js
CHANGED
@@ -20,16 +20,15 @@ const rulesDirCache = {};
|
|
20
20
|
|
21
21
|
/**
|
22
22
|
* Load all rule modules from specified directory.
|
23
|
-
* @param {string} [
|
23
|
+
* @param {string} [relativeRulesDir] Path to rules directory, may be relative. Defaults to `lib/rules`.
|
24
24
|
* @param {string} cwd Current working directory
|
25
25
|
* @returns {Object} Loaded rule modules by rule ids (file names).
|
26
26
|
*/
|
27
|
-
module.exports = function(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
}
|
27
|
+
module.exports = function(relativeRulesDir, cwd) {
|
28
|
+
|
29
|
+
const rulesDir = relativeRulesDir
|
30
|
+
? path.resolve(cwd, relativeRulesDir)
|
31
|
+
: path.join(__dirname, "rules");
|
33
32
|
|
34
33
|
// cache will help performance as IO operation are expensive
|
35
34
|
if (rulesDirCache[rulesDir]) {
|
@@ -58,11 +58,11 @@ function isPropertyDescriptor(node) {
|
|
58
58
|
* Object.defineProperties(obj, {foo: {set: ...}})
|
59
59
|
* Object.create(proto, {foo: {set: ...}})
|
60
60
|
*/
|
61
|
-
|
61
|
+
const grandparent = node.parent.parent;
|
62
62
|
|
63
|
-
return
|
64
|
-
isArgumentOfMethodCall(
|
65
|
-
isArgumentOfMethodCall(
|
63
|
+
return grandparent.type === "ObjectExpression" && (
|
64
|
+
isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
|
65
|
+
isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
|
66
66
|
);
|
67
67
|
}
|
68
68
|
|
@@ -71,8 +71,10 @@ function isTargetMethod(node) {
|
|
71
71
|
* @returns {boolean} `true` if the node is the callback of an array method.
|
72
72
|
*/
|
73
73
|
function isCallbackOfArrayMethod(node) {
|
74
|
-
|
75
|
-
|
74
|
+
let currentNode = node;
|
75
|
+
|
76
|
+
while (currentNode) {
|
77
|
+
const parent = currentNode.parent;
|
76
78
|
|
77
79
|
switch (parent.type) {
|
78
80
|
|
@@ -82,7 +84,7 @@ function isCallbackOfArrayMethod(node) {
|
|
82
84
|
*/
|
83
85
|
case "LogicalExpression":
|
84
86
|
case "ConditionalExpression":
|
85
|
-
|
87
|
+
currentNode = parent;
|
86
88
|
break;
|
87
89
|
|
88
90
|
/*
|
@@ -99,7 +101,7 @@ function isCallbackOfArrayMethod(node) {
|
|
99
101
|
if (func === null || !astUtils.isCallee(func)) {
|
100
102
|
return false;
|
101
103
|
}
|
102
|
-
|
104
|
+
currentNode = func.parent;
|
103
105
|
break;
|
104
106
|
}
|
105
107
|
|
@@ -112,13 +114,13 @@ function isCallbackOfArrayMethod(node) {
|
|
112
114
|
if (astUtils.isArrayFromMethod(parent.callee)) {
|
113
115
|
return (
|
114
116
|
parent.arguments.length >= 2 &&
|
115
|
-
parent.arguments[1] ===
|
117
|
+
parent.arguments[1] === currentNode
|
116
118
|
);
|
117
119
|
}
|
118
120
|
if (isTargetMethod(parent.callee)) {
|
119
121
|
return (
|
120
122
|
parent.arguments.length >= 1 &&
|
121
|
-
parent.arguments[0] ===
|
123
|
+
parent.arguments[0] === currentNode
|
122
124
|
);
|
123
125
|
}
|
124
126
|
return false;
|
@@ -59,16 +59,16 @@ module.exports = {
|
|
59
59
|
/**
|
60
60
|
* Normalizes a given option value.
|
61
61
|
*
|
62
|
-
* @param {string|Object|undefined}
|
62
|
+
* @param {string|Object|undefined} providedOption - An option value to parse.
|
63
63
|
* @returns {{multiline: boolean, minItems: number}} Normalized option object.
|
64
64
|
*/
|
65
|
-
function normalizeOptionValue(
|
65
|
+
function normalizeOptionValue(providedOption) {
|
66
66
|
let multiline = false;
|
67
67
|
let minItems;
|
68
68
|
|
69
|
-
option =
|
69
|
+
const option = providedOption || "always";
|
70
70
|
|
71
|
-
if (option === "always" || option.minItems === 0) {
|
71
|
+
if (!option || option === "always" || option.minItems === 0) {
|
72
72
|
minItems = 0;
|
73
73
|
} else if (option === "never") {
|
74
74
|
minItems = Number.POSITIVE_INFINITY;
|
package/lib/rules/curly.js
CHANGED
@@ -137,12 +137,14 @@ module.exports = {
|
|
137
137
|
return true;
|
138
138
|
}
|
139
139
|
|
140
|
-
|
141
|
-
|
142
|
-
|
140
|
+
for (
|
141
|
+
let currentNode = node.consequent.body[0];
|
142
|
+
currentNode;
|
143
|
+
currentNode = astUtils.getTrailingStatement(currentNode)
|
144
|
+
) {
|
145
|
+
if (currentNode.type === "IfStatement" && !currentNode.alternate) {
|
143
146
|
return true;
|
144
147
|
}
|
145
|
-
node = astUtils.getTrailingStatement(node);
|
146
148
|
}
|
147
149
|
}
|
148
150
|
|
@@ -311,14 +313,13 @@ module.exports = {
|
|
311
313
|
function prepareIfChecks(node) {
|
312
314
|
const preparedChecks = [];
|
313
315
|
|
314
|
-
|
315
|
-
preparedChecks.push(prepareCheck(
|
316
|
-
if (
|
317
|
-
preparedChecks.push(prepareCheck(
|
316
|
+
for (let currentNode = node; currentNode; currentNode = currentNode.alternate) {
|
317
|
+
preparedChecks.push(prepareCheck(currentNode, currentNode.consequent, "if", { condition: true }));
|
318
|
+
if (currentNode.alternate && currentNode.alternate.type !== "IfStatement") {
|
319
|
+
preparedChecks.push(prepareCheck(currentNode, currentNode.alternate, "else"));
|
318
320
|
break;
|
319
321
|
}
|
320
|
-
|
321
|
-
} while (node);
|
322
|
+
}
|
322
323
|
|
323
324
|
if (consistent) {
|
324
325
|
|
@@ -85,7 +85,6 @@ module.exports = {
|
|
85
85
|
}
|
86
86
|
|
87
87
|
const modes = (function(option) {
|
88
|
-
option = option || {};
|
89
88
|
const defaults = optionToDefinition(option, optionDefinitions.before);
|
90
89
|
|
91
90
|
return {
|
@@ -93,7 +92,7 @@ module.exports = {
|
|
93
92
|
anonymous: optionToDefinition(option.anonymous, defaults),
|
94
93
|
method: optionToDefinition(option.method, defaults)
|
95
94
|
};
|
96
|
-
}(context.options[0]));
|
95
|
+
}(context.options[0] || {}));
|
97
96
|
|
98
97
|
const sourceCode = context.getSourceCode();
|
99
98
|
|
@@ -505,12 +505,9 @@ module.exports = {
|
|
505
505
|
*/
|
506
506
|
function getParentNodeByType(node, type, stopAtList) {
|
507
507
|
let parent = node.parent;
|
508
|
+
const stopAtSet = new Set(stopAtList || ["Program"]);
|
508
509
|
|
509
|
-
|
510
|
-
stopAtList = ["Program"];
|
511
|
-
}
|
512
|
-
|
513
|
-
while (parent.type !== type && stopAtList.indexOf(parent.type) === -1 && parent.type !== "Program") {
|
510
|
+
while (parent.type !== type && !stopAtSet.has(parent.type) && parent.type !== "Program") {
|
514
511
|
parent = parent.parent;
|
515
512
|
}
|
516
513
|
|
@@ -941,19 +938,19 @@ module.exports = {
|
|
941
938
|
/**
|
942
939
|
* Returns the expected indentation for the case statement
|
943
940
|
* @param {ASTNode} node node to examine
|
944
|
-
* @param {int} [
|
941
|
+
* @param {int} [providedSwitchIndent] indent for switch statement
|
945
942
|
* @returns {int} indent size
|
946
943
|
*/
|
947
|
-
function expectedCaseIndent(node,
|
944
|
+
function expectedCaseIndent(node, providedSwitchIndent) {
|
948
945
|
const switchNode = (node.type === "SwitchStatement") ? node : node.parent;
|
946
|
+
const switchIndent = typeof providedSwitchIndent === "undefined"
|
947
|
+
? getNodeIndent(switchNode).goodChar
|
948
|
+
: providedSwitchIndent;
|
949
949
|
let caseIndent;
|
950
950
|
|
951
951
|
if (caseIndentStore[switchNode.loc.start.line]) {
|
952
952
|
return caseIndentStore[switchNode.loc.start.line];
|
953
953
|
}
|
954
|
-
if (typeof switchIndent === "undefined") {
|
955
|
-
switchIndent = getNodeIndent(switchNode).goodChar;
|
956
|
-
}
|
957
954
|
|
958
955
|
if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
|
959
956
|
caseIndent = switchIndent;
|
package/lib/rules/indent.js
CHANGED
@@ -779,6 +779,19 @@ module.exports = {
|
|
779
779
|
return (statement.type === "ExpressionStatement" || statement.type === "VariableDeclaration") && statement.parent.type === "Program";
|
780
780
|
}
|
781
781
|
|
782
|
+
/**
|
783
|
+
* Counts the number of linebreaks that follow the last non-whitespace character in a string
|
784
|
+
* @param {string} string The string to check
|
785
|
+
* @returns {number} The number of JavaScript linebreaks that follow the last non-whitespace character,
|
786
|
+
* or the total number of linebreaks if the string is all whitespace.
|
787
|
+
*/
|
788
|
+
function countTrailingLinebreaks(string) {
|
789
|
+
const trailingWhitespace = string.match(/\s*$/)[0];
|
790
|
+
const linebreakMatches = trailingWhitespace.match(astUtils.createGlobalLinebreakMatcher());
|
791
|
+
|
792
|
+
return linebreakMatches === null ? 0 : linebreakMatches.length;
|
793
|
+
}
|
794
|
+
|
782
795
|
/**
|
783
796
|
* Check indentation for lists of elements (arrays, objects, function params)
|
784
797
|
* @param {ASTNode[]} elements List of elements that should be offset
|
@@ -836,8 +849,12 @@ module.exports = {
|
|
836
849
|
} else {
|
837
850
|
const previousElement = elements[index - 1];
|
838
851
|
const firstTokenOfPreviousElement = previousElement && getFirstToken(previousElement);
|
852
|
+
const previousElementLastToken = previousElement && sourceCode.getLastToken(previousElement);
|
839
853
|
|
840
|
-
if (
|
854
|
+
if (
|
855
|
+
previousElement &&
|
856
|
+
previousElementLastToken.loc.end.line - countTrailingLinebreaks(previousElementLastToken.value) > startToken.loc.end.line
|
857
|
+
) {
|
841
858
|
offsets.setDesiredOffsets(element.range, firstTokenOfPreviousElement, 0);
|
842
859
|
}
|
843
860
|
}
|
@@ -980,6 +997,8 @@ module.exports = {
|
|
980
997
|
return !node || node.loc.start.line === token.loc.start.line;
|
981
998
|
}
|
982
999
|
|
1000
|
+
const ignoredNodeFirstTokens = new Set();
|
1001
|
+
|
983
1002
|
const baseOffsetListeners = {
|
984
1003
|
"ArrayExpression, ArrayPattern"(node) {
|
985
1004
|
const openingBracket = sourceCode.getFirstToken(node);
|
@@ -1010,15 +1029,6 @@ module.exports = {
|
|
1010
1029
|
addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
|
1011
1030
|
}
|
1012
1031
|
addBlocklessNodeIndent(node.body);
|
1013
|
-
|
1014
|
-
let arrowToken;
|
1015
|
-
|
1016
|
-
if (node.params.length) {
|
1017
|
-
arrowToken = sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isArrowToken);
|
1018
|
-
} else {
|
1019
|
-
arrowToken = sourceCode.getFirstToken(node, astUtils.isArrowToken);
|
1020
|
-
}
|
1021
|
-
offsets.setDesiredOffset(arrowToken, sourceCode.getFirstToken(node), 0);
|
1022
1032
|
},
|
1023
1033
|
|
1024
1034
|
AssignmentExpression(node) {
|
@@ -1128,9 +1138,6 @@ module.exports = {
|
|
1128
1138
|
*/
|
1129
1139
|
offsets.setDesiredOffset(firstAlternateToken, firstToken, 1);
|
1130
1140
|
}
|
1131
|
-
|
1132
|
-
offsets.setDesiredOffsets([questionMarkToken.range[1], colonToken.range[0]], firstConsequentToken, 0);
|
1133
|
-
offsets.setDesiredOffsets([colonToken.range[1], node.range[1]], firstAlternateToken, 0);
|
1134
1141
|
}
|
1135
1142
|
},
|
1136
1143
|
|
@@ -1272,20 +1279,9 @@ module.exports = {
|
|
1272
1279
|
SwitchStatement(node) {
|
1273
1280
|
const openingCurly = sourceCode.getTokenAfter(node.discriminant, astUtils.isOpeningBraceToken);
|
1274
1281
|
const closingCurly = sourceCode.getLastToken(node);
|
1275
|
-
const caseKeywords = node.cases.map(switchCase => sourceCode.getFirstToken(switchCase));
|
1276
1282
|
|
1277
1283
|
offsets.setDesiredOffsets([openingCurly.range[1], closingCurly.range[0]], openingCurly, options.SwitchCase);
|
1278
1284
|
|
1279
|
-
node.cases.forEach((switchCase, index) => {
|
1280
|
-
const caseKeyword = caseKeywords[index];
|
1281
|
-
|
1282
|
-
if (!(switchCase.consequent.length === 1 && switchCase.consequent[0].type === "BlockStatement")) {
|
1283
|
-
const tokenAfterCurrentCase = index === node.cases.length - 1 ? closingCurly : caseKeywords[index + 1];
|
1284
|
-
|
1285
|
-
offsets.setDesiredOffsets([caseKeyword.range[1], tokenAfterCurrentCase.range[0]], caseKeyword, 1);
|
1286
|
-
}
|
1287
|
-
});
|
1288
|
-
|
1289
1285
|
if (node.cases.length) {
|
1290
1286
|
sourceCode.getTokensBetween(
|
1291
1287
|
node.cases[node.cases.length - 1],
|
@@ -1295,6 +1291,15 @@ module.exports = {
|
|
1295
1291
|
}
|
1296
1292
|
},
|
1297
1293
|
|
1294
|
+
SwitchCase(node) {
|
1295
|
+
if (!(node.consequent.length === 1 && node.consequent[0].type === "BlockStatement")) {
|
1296
|
+
const caseKeyword = sourceCode.getFirstToken(node);
|
1297
|
+
const tokenAfterCurrentCase = sourceCode.getTokenAfter(node);
|
1298
|
+
|
1299
|
+
offsets.setDesiredOffsets([caseKeyword.range[1], tokenAfterCurrentCase.range[0]], caseKeyword, 1);
|
1300
|
+
}
|
1301
|
+
},
|
1302
|
+
|
1298
1303
|
TemplateLiteral(node) {
|
1299
1304
|
node.expressions.forEach((expression, index) => {
|
1300
1305
|
const previousQuasi = node.quasis[index];
|
@@ -1385,7 +1390,6 @@ module.exports = {
|
|
1385
1390
|
const firstToken = sourceCode.getFirstToken(node);
|
1386
1391
|
|
1387
1392
|
offsets.setDesiredOffsets(node.name.range, firstToken, 1);
|
1388
|
-
offsets.setDesiredOffset(sourceCode.getLastToken(node), firstToken, 0);
|
1389
1393
|
},
|
1390
1394
|
|
1391
1395
|
JSXExpressionContainer(node) {
|
@@ -1397,7 +1401,15 @@ module.exports = {
|
|
1397
1401
|
openingCurly,
|
1398
1402
|
1
|
1399
1403
|
);
|
1400
|
-
|
1404
|
+
},
|
1405
|
+
|
1406
|
+
"*"(node) {
|
1407
|
+
const firstToken = sourceCode.getFirstToken(node);
|
1408
|
+
|
1409
|
+
// Ensure that the children of every node are indented at least as much as the first token.
|
1410
|
+
if (firstToken && !ignoredNodeFirstTokens.has(firstToken)) {
|
1411
|
+
offsets.setDesiredOffsets(node.range, firstToken, 0);
|
1412
|
+
}
|
1401
1413
|
}
|
1402
1414
|
};
|
1403
1415
|
|
@@ -1406,7 +1418,8 @@ module.exports = {
|
|
1406
1418
|
/*
|
1407
1419
|
* To ignore the indentation of a node:
|
1408
1420
|
* 1. Don't call the node's listener when entering it (if it has a listener)
|
1409
|
-
* 2.
|
1421
|
+
* 2. Don't set any offsets against the first token of the node.
|
1422
|
+
* 3. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
|
1410
1423
|
*/
|
1411
1424
|
const offsetListeners = lodash.mapValues(
|
1412
1425
|
baseOffsetListeners,
|
@@ -1434,7 +1447,16 @@ module.exports = {
|
|
1434
1447
|
|
1435
1448
|
// For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
|
1436
1449
|
const ignoredNodes = new Set();
|
1437
|
-
|
1450
|
+
|
1451
|
+
/**
|
1452
|
+
* Ignores a node
|
1453
|
+
* @param {ASTNode} node The node to ignore
|
1454
|
+
* @returns {void}
|
1455
|
+
*/
|
1456
|
+
function addToIgnoredNodes(node) {
|
1457
|
+
ignoredNodes.add(node);
|
1458
|
+
ignoredNodeFirstTokens.add(sourceCode.getFirstToken(node));
|
1459
|
+
}
|
1438
1460
|
|
1439
1461
|
const ignoredNodeListeners = options.ignoredNodes.reduce(
|
1440
1462
|
(listeners, ignoredSelector) => Object.assign(listeners, { [ignoredSelector]: addToIgnoredNodes }),
|
@@ -1457,7 +1479,7 @@ module.exports = {
|
|
1457
1479
|
|
1458
1480
|
// If a node's type is nonstandard, we can't tell how its children should be offset, so ignore it.
|
1459
1481
|
if (!KNOWN_NODES.has(node.type)) {
|
1460
|
-
|
1482
|
+
addToIgnoredNodes(node);
|
1461
1483
|
}
|
1462
1484
|
},
|
1463
1485
|
"Program:exit"() {
|
@@ -108,13 +108,10 @@ module.exports = {
|
|
108
108
|
* Reports a given token if there are not space(s) before the token.
|
109
109
|
*
|
110
110
|
* @param {Token} token - A token to report.
|
111
|
-
* @param {RegExp
|
112
|
-
* token to check.
|
111
|
+
* @param {RegExp} pattern - A pattern of the previous token to check.
|
113
112
|
* @returns {void}
|
114
113
|
*/
|
115
114
|
function expectSpaceBefore(token, pattern) {
|
116
|
-
pattern = pattern || PREV_TOKEN;
|
117
|
-
|
118
115
|
const prevToken = sourceCode.getTokenBefore(token);
|
119
116
|
|
120
117
|
if (prevToken &&
|
@@ -138,13 +135,10 @@ module.exports = {
|
|
138
135
|
* Reports a given token if there are space(s) before the token.
|
139
136
|
*
|
140
137
|
* @param {Token} token - A token to report.
|
141
|
-
* @param {RegExp
|
142
|
-
* token to check.
|
138
|
+
* @param {RegExp} pattern - A pattern of the previous token to check.
|
143
139
|
* @returns {void}
|
144
140
|
*/
|
145
141
|
function unexpectSpaceBefore(token, pattern) {
|
146
|
-
pattern = pattern || PREV_TOKEN;
|
147
|
-
|
148
142
|
const prevToken = sourceCode.getTokenBefore(token);
|
149
143
|
|
150
144
|
if (prevToken &&
|
@@ -168,13 +162,10 @@ module.exports = {
|
|
168
162
|
* Reports a given token if there are not space(s) after the token.
|
169
163
|
*
|
170
164
|
* @param {Token} token - A token to report.
|
171
|
-
* @param {RegExp
|
172
|
-
* token to check.
|
165
|
+
* @param {RegExp} pattern - A pattern of the next token to check.
|
173
166
|
* @returns {void}
|
174
167
|
*/
|
175
168
|
function expectSpaceAfter(token, pattern) {
|
176
|
-
pattern = pattern || NEXT_TOKEN;
|
177
|
-
|
178
169
|
const nextToken = sourceCode.getTokenAfter(token);
|
179
170
|
|
180
171
|
if (nextToken &&
|
@@ -198,13 +189,10 @@ module.exports = {
|
|
198
189
|
* Reports a given token if there are space(s) after the token.
|
199
190
|
*
|
200
191
|
* @param {Token} token - A token to report.
|
201
|
-
* @param {RegExp
|
202
|
-
* token to check.
|
192
|
+
* @param {RegExp} pattern - A pattern of the next token to check.
|
203
193
|
* @returns {void}
|
204
194
|
*/
|
205
195
|
function unexpectSpaceAfter(token, pattern) {
|
206
|
-
pattern = pattern || NEXT_TOKEN;
|
207
|
-
|
208
196
|
const nextToken = sourceCode.getTokenAfter(token);
|
209
197
|
|
210
198
|
if (nextToken &&
|
@@ -274,7 +262,7 @@ module.exports = {
|
|
274
262
|
* @returns {void}
|
275
263
|
*/
|
276
264
|
function checkSpacingBefore(token, pattern) {
|
277
|
-
checkMethodMap[token.value].before(token, pattern);
|
265
|
+
checkMethodMap[token.value].before(token, pattern || PREV_TOKEN);
|
278
266
|
}
|
279
267
|
|
280
268
|
/**
|
@@ -287,7 +275,7 @@ module.exports = {
|
|
287
275
|
* @returns {void}
|
288
276
|
*/
|
289
277
|
function checkSpacingAfter(token, pattern) {
|
290
|
-
checkMethodMap[token.value].after(token, pattern);
|
278
|
+
checkMethodMap[token.value].after(token, pattern || NEXT_TOKEN);
|
291
279
|
}
|
292
280
|
|
293
281
|
/**
|
package/lib/rules/max-len.js
CHANGED
@@ -213,7 +213,8 @@ module.exports = {
|
|
213
213
|
* @returns {ASTNode[]} An array of string nodes.
|
214
214
|
*/
|
215
215
|
function getAllStrings() {
|
216
|
-
return sourceCode.ast.tokens.filter(token => token.type === "String"
|
216
|
+
return sourceCode.ast.tokens.filter(token => (token.type === "String" ||
|
217
|
+
(token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute")));
|
217
218
|
}
|
218
219
|
|
219
220
|
/**
|
@@ -287,6 +288,7 @@ module.exports = {
|
|
287
288
|
* line is a comment
|
288
289
|
*/
|
289
290
|
let lineIsComment = false;
|
291
|
+
let textToMeasure;
|
290
292
|
|
291
293
|
/*
|
292
294
|
* We can short-circuit the comment checks if we're already out of
|
@@ -305,12 +307,17 @@ module.exports = {
|
|
305
307
|
|
306
308
|
if (isFullLineComment(line, lineNumber, comment)) {
|
307
309
|
lineIsComment = true;
|
310
|
+
textToMeasure = line;
|
308
311
|
} else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
|
309
|
-
|
312
|
+
textToMeasure = stripTrailingComment(line, comment);
|
313
|
+
} else {
|
314
|
+
textToMeasure = line;
|
310
315
|
}
|
316
|
+
} else {
|
317
|
+
textToMeasure = line;
|
311
318
|
}
|
312
|
-
if (ignorePattern && ignorePattern.test(
|
313
|
-
ignoreUrls && URL_REGEXP.test(
|
319
|
+
if (ignorePattern && ignorePattern.test(textToMeasure) ||
|
320
|
+
ignoreUrls && URL_REGEXP.test(textToMeasure) ||
|
314
321
|
ignoreStrings && stringsByLine[lineNumber] ||
|
315
322
|
ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] ||
|
316
323
|
ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]
|
@@ -320,7 +327,7 @@ module.exports = {
|
|
320
327
|
return;
|
321
328
|
}
|
322
329
|
|
323
|
-
const lineLength = computeLineLength(
|
330
|
+
const lineLength = computeLineLength(textToMeasure, tabWidth);
|
324
331
|
const commentLengthApplies = lineIsComment && maxCommentLength;
|
325
332
|
|
326
333
|
if (lineIsComment && ignoreComments) {
|
@@ -11,7 +11,7 @@
|
|
11
11
|
module.exports = {
|
12
12
|
meta: {
|
13
13
|
docs: {
|
14
|
-
description: "disallow use of the Buffer() constructor",
|
14
|
+
description: "disallow use of the `Buffer()` constructor",
|
15
15
|
category: "Node.js and CommonJS",
|
16
16
|
recommended: false,
|
17
17
|
url: "https://eslint.org/docs/rules/no-buffer-constructor"
|
@@ -5,6 +5,44 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
const RegExpValidator = require("regexpp").RegExpValidator;
|
9
|
+
const collector = new class {
|
10
|
+
constructor() {
|
11
|
+
this.ecmaVersion = 2018;
|
12
|
+
this._source = "";
|
13
|
+
this._controlChars = [];
|
14
|
+
this._validator = new RegExpValidator(this);
|
15
|
+
}
|
16
|
+
|
17
|
+
onPatternEnter() {
|
18
|
+
this._controlChars = [];
|
19
|
+
}
|
20
|
+
|
21
|
+
onCharacter(start, end, cp) {
|
22
|
+
if (cp >= 0x00 &&
|
23
|
+
cp <= 0x1F &&
|
24
|
+
(
|
25
|
+
this._source.codePointAt(start) === cp ||
|
26
|
+
this._source.slice(start, end).startsWith("\\x") ||
|
27
|
+
this._source.slice(start, end).startsWith("\\u")
|
28
|
+
)
|
29
|
+
) {
|
30
|
+
this._controlChars.push(`\\x${`0${cp.toString(16)}`.slice(-2)}`);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
collectControlChars(regexpStr) {
|
35
|
+
try {
|
36
|
+
this._source = regexpStr;
|
37
|
+
this._validator.validatePattern(regexpStr); // Call onCharacter hook
|
38
|
+
} catch (err) {
|
39
|
+
|
40
|
+
// Ignore syntax errors in RegExp.
|
41
|
+
}
|
42
|
+
return this._controlChars;
|
43
|
+
}
|
44
|
+
}();
|
45
|
+
|
8
46
|
//------------------------------------------------------------------------------
|
9
47
|
// Rule Definition
|
10
48
|
//------------------------------------------------------------------------------
|
@@ -33,87 +71,28 @@ module.exports = {
|
|
33
71
|
* @returns {RegExp|null} Regex if found else null
|
34
72
|
* @private
|
35
73
|
*/
|
36
|
-
function
|
37
|
-
if (node.
|
38
|
-
return node.
|
74
|
+
function getRegExpPattern(node) {
|
75
|
+
if (node.regex) {
|
76
|
+
return node.regex.pattern;
|
39
77
|
}
|
40
|
-
if (typeof node.value === "string"
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
// there could be an invalid regular expression string
|
49
|
-
try {
|
50
|
-
return new RegExp(node.value);
|
51
|
-
} catch (ex) {
|
52
|
-
return null;
|
53
|
-
}
|
54
|
-
}
|
78
|
+
if (typeof node.value === "string" &&
|
79
|
+
(node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
|
80
|
+
node.parent.callee.type === "Identifier" &&
|
81
|
+
node.parent.callee.name === "RegExp" &&
|
82
|
+
node.parent.arguments[0] === node
|
83
|
+
) {
|
84
|
+
return node.value;
|
55
85
|
}
|
56
86
|
|
57
87
|
return null;
|
58
88
|
}
|
59
89
|
|
60
|
-
|
61
|
-
const controlChar = /[\x00-\x1f]/g; // eslint-disable-line no-control-regex
|
62
|
-
const consecutiveSlashes = /\\+/g;
|
63
|
-
const consecutiveSlashesAtEnd = /\\+$/g;
|
64
|
-
const stringControlChar = /\\x[01][0-9a-f]/ig;
|
65
|
-
const stringControlCharWithoutSlash = /x[01][0-9a-f]/ig;
|
66
|
-
|
67
|
-
/**
|
68
|
-
* Return a list of the control characters in the given regex string
|
69
|
-
* @param {string} regexStr regex as string to check
|
70
|
-
* @returns {array} returns a list of found control characters on given string
|
71
|
-
* @private
|
72
|
-
*/
|
73
|
-
function getControlCharacters(regexStr) {
|
74
|
-
|
75
|
-
// check control characters, if RegExp object used
|
76
|
-
const controlChars = regexStr.match(controlChar) || [];
|
77
|
-
|
78
|
-
let stringControlChars = [];
|
79
|
-
|
80
|
-
// check substr, if regex literal used
|
81
|
-
const subStrIndex = regexStr.search(stringControlChar);
|
82
|
-
|
83
|
-
if (subStrIndex > -1) {
|
84
|
-
|
85
|
-
// is it escaped, check backslash count
|
86
|
-
const possibleEscapeCharacters = regexStr.slice(0, subStrIndex).match(consecutiveSlashesAtEnd);
|
87
|
-
|
88
|
-
const hasControlChars = possibleEscapeCharacters === null || !(possibleEscapeCharacters[0].length % 2);
|
89
|
-
|
90
|
-
if (hasControlChars) {
|
91
|
-
stringControlChars = regexStr.slice(subStrIndex, -1)
|
92
|
-
.split(consecutiveSlashes)
|
93
|
-
.filter(Boolean)
|
94
|
-
.map(x => {
|
95
|
-
const match = x.match(stringControlCharWithoutSlash) || [x];
|
96
|
-
|
97
|
-
return `\\${match[0]}`;
|
98
|
-
});
|
99
|
-
}
|
100
|
-
}
|
101
|
-
|
102
|
-
return controlChars.map(x => {
|
103
|
-
const hexCode = `0${x.charCodeAt(0).toString(16)}`.slice(-2);
|
104
|
-
|
105
|
-
return `\\x${hexCode}`;
|
106
|
-
}).concat(stringControlChars);
|
107
|
-
}
|
108
|
-
|
109
90
|
return {
|
110
91
|
Literal(node) {
|
111
|
-
const
|
112
|
-
|
113
|
-
if (regex) {
|
114
|
-
const computedValue = regex.toString();
|
92
|
+
const pattern = getRegExpPattern(node);
|
115
93
|
|
116
|
-
|
94
|
+
if (pattern) {
|
95
|
+
const controlCharacters = collector.collectControlChars(pattern);
|
117
96
|
|
118
97
|
if (controlCharacters.length > 0) {
|
119
98
|
context.report({
|