eslint 3.15.0 → 3.17.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 +79 -0
- package/conf/{eslint.json → eslint-recommended.js} +87 -71
- package/lib/ast-utils.js +182 -80
- package/lib/code-path-analysis/code-path-state.js +2 -2
- package/lib/config/autoconfig.js +3 -3
- package/lib/config/config-file.js +14 -7
- package/lib/config/config-initializer.js +1 -1
- package/lib/config.js +3 -2
- package/lib/eslint.js +4 -4
- package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
- package/lib/rules/array-callback-return.js +15 -5
- package/lib/rules/arrow-body-style.js +7 -4
- package/lib/rules/arrow-spacing.js +7 -6
- package/lib/rules/block-spacing.js +2 -2
- package/lib/rules/brace-style.js +2 -6
- package/lib/rules/capitalized-comments.js +8 -7
- package/lib/rules/comma-spacing.js +3 -3
- package/lib/rules/complexity.js +14 -8
- package/lib/rules/consistent-return.js +18 -11
- package/lib/rules/constructor-super.js +3 -3
- package/lib/rules/curly.js +11 -7
- package/lib/rules/default-case.js +3 -3
- package/lib/rules/eqeqeq.js +15 -6
- package/lib/rules/func-call-spacing.js +10 -13
- package/lib/rules/func-names.js +20 -5
- package/lib/rules/generator-star-spacing.js +18 -19
- package/lib/rules/id-blacklist.js +2 -2
- package/lib/rules/id-length.js +3 -3
- package/lib/rules/id-match.js +2 -2
- package/lib/rules/indent.js +7 -6
- package/lib/rules/key-spacing.js +12 -16
- package/lib/rules/keyword-spacing.js +21 -17
- package/lib/rules/line-comment-position.js +16 -6
- package/lib/rules/linebreak-style.js +7 -1
- package/lib/rules/lines-around-comment.js +23 -4
- package/lib/rules/lines-around-directive.js +3 -3
- package/lib/rules/max-lines.js +2 -2
- package/lib/rules/max-params.js +17 -4
- package/lib/rules/max-statements-per-line.js +7 -6
- package/lib/rules/max-statements.js +11 -10
- package/lib/rules/newline-after-var.js +7 -2
- package/lib/rules/newline-per-chained-call.js +3 -1
- package/lib/rules/no-compare-neg-zero.js +53 -0
- package/lib/rules/no-cond-assign.js +3 -3
- package/lib/rules/no-else-return.js +13 -1
- package/lib/rules/no-empty-function.js +9 -16
- package/lib/rules/no-extend-native.js +3 -3
- package/lib/rules/no-extra-bind.js +3 -4
- package/lib/rules/no-extra-boolean-cast.js +8 -0
- package/lib/rules/no-extra-parens.js +1 -2
- package/lib/rules/no-extra-semi.js +13 -1
- package/lib/rules/no-inner-declarations.js +4 -4
- package/lib/rules/no-invalid-regexp.js +2 -1
- package/lib/rules/no-irregular-whitespace.js +7 -1
- package/lib/rules/no-lone-blocks.js +10 -10
- package/lib/rules/no-mixed-operators.js +1 -7
- package/lib/rules/no-multi-spaces.js +4 -1
- package/lib/rules/no-multi-str.js +7 -3
- package/lib/rules/no-multiple-empty-lines.js +2 -4
- package/lib/rules/no-param-reassign.js +29 -6
- package/lib/rules/no-restricted-properties.js +2 -0
- package/lib/rules/no-return-assign.js +7 -14
- package/lib/rules/no-return-await.js +1 -1
- package/lib/rules/no-sequences.js +7 -6
- package/lib/rules/no-trailing-spaces.js +8 -2
- package/lib/rules/no-undefined.js +45 -6
- package/lib/rules/no-unexpected-multiline.js +9 -8
- package/lib/rules/no-unneeded-ternary.js +5 -1
- package/lib/rules/no-unused-labels.js +17 -2
- package/lib/rules/no-unused-vars.js +13 -27
- package/lib/rules/no-use-before-define.js +1 -1
- package/lib/rules/no-useless-computed-key.js +8 -3
- package/lib/rules/no-useless-concat.js +10 -7
- package/lib/rules/no-useless-escape.js +2 -2
- package/lib/rules/no-useless-return.js +14 -9
- package/lib/rules/no-var.js +1 -3
- package/lib/rules/no-whitespace-before-property.js +5 -16
- package/lib/rules/nonblock-statement-body-position.js +114 -0
- package/lib/rules/object-curly-newline.js +2 -2
- package/lib/rules/object-curly-spacing.js +7 -25
- package/lib/rules/object-property-newline.js +3 -3
- package/lib/rules/object-shorthand.js +4 -3
- package/lib/rules/operator-assignment.js +2 -2
- package/lib/rules/operator-linebreak.js +8 -10
- package/lib/rules/padded-blocks.js +39 -30
- package/lib/rules/prefer-destructuring.js +1 -1
- package/lib/rules/prefer-spread.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/quotes.js +10 -6
- package/lib/rules/semi-spacing.js +4 -0
- package/lib/rules/semi.js +13 -1
- package/lib/rules/space-before-function-paren.js +8 -5
- package/lib/rules/space-unary-ops.js +19 -1
- package/lib/rules/spaced-comment.js +2 -2
- package/lib/rules/strict.js +10 -4
- package/lib/rules/unicode-bom.js +1 -1
- package/lib/rules/wrap-iife.js +5 -5
- package/lib/rules/yoda.js +4 -9
- package/lib/testers/rule-tester.js +46 -9
- package/lib/token-store/backward-token-comment-cursor.js +57 -0
- package/lib/token-store/backward-token-cursor.js +56 -0
- package/lib/token-store/cursor.js +76 -0
- package/lib/token-store/cursors.js +92 -0
- package/lib/token-store/decorative-cursor.js +39 -0
- package/lib/token-store/filter-cursor.js +43 -0
- package/lib/token-store/forward-token-comment-cursor.js +57 -0
- package/lib/token-store/forward-token-cursor.js +61 -0
- package/lib/token-store/index.js +604 -0
- package/lib/token-store/limit-cursor.js +40 -0
- package/lib/token-store/padded-token-cursor.js +38 -0
- package/lib/token-store/skip-cursor.js +42 -0
- package/lib/token-store/utils.js +100 -0
- package/lib/util/fix-tracker.js +121 -0
- package/lib/util/source-code-fixer.js +35 -39
- package/lib/util/source-code.js +129 -16
- package/messages/extend-config-missing.txt +3 -0
- package/package.json +5 -6
- package/lib/token-store.js +0 -203
@@ -5,14 +5,16 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
8
12
|
const astUtils = require("../ast-utils");
|
9
13
|
|
10
14
|
//------------------------------------------------------------------------------
|
11
15
|
// Rule Definition
|
12
16
|
//------------------------------------------------------------------------------
|
13
17
|
|
14
|
-
const LINEBREAK_REGEX = /\r\n|\r|\n|\u2028|\u2029/g;
|
15
|
-
|
16
18
|
module.exports = {
|
17
19
|
meta: {
|
18
20
|
docs: {
|
@@ -85,7 +87,7 @@ module.exports = {
|
|
85
87
|
if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
|
86
88
|
|
87
89
|
// If there is a comment before and after the operator, don't do a fix.
|
88
|
-
if (sourceCode.
|
90
|
+
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore && sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
|
89
91
|
return null;
|
90
92
|
}
|
91
93
|
|
@@ -100,6 +102,7 @@ module.exports = {
|
|
100
102
|
newTextBefore = textAfter;
|
101
103
|
newTextAfter = textBefore;
|
102
104
|
} else {
|
105
|
+
const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();
|
103
106
|
|
104
107
|
// Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
|
105
108
|
newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
|
@@ -129,19 +132,14 @@ module.exports = {
|
|
129
132
|
* @returns {void}
|
130
133
|
*/
|
131
134
|
function validateNode(node, leftSide) {
|
132
|
-
let leftToken = sourceCode.getLastToken(leftSide);
|
133
|
-
let operatorToken = sourceCode.getTokenAfter(leftToken);
|
134
135
|
|
135
136
|
// When the left part of a binary expression is a single expression wrapped in
|
136
137
|
// parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
|
137
138
|
// and operatorToken will be the closing parenthesis.
|
138
139
|
// The leftToken should be the last closing parenthesis, and the operatorToken
|
139
140
|
// should be the token right after that.
|
140
|
-
|
141
|
-
|
142
|
-
operatorToken = sourceCode.getTokenAfter(operatorToken);
|
143
|
-
}
|
144
|
-
|
141
|
+
const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
|
142
|
+
const leftToken = sourceCode.getTokenBefore(operatorToken);
|
145
143
|
const rightToken = sourceCode.getTokenAfter(operatorToken);
|
146
144
|
const operator = operatorToken.value;
|
147
145
|
const operatorStyleOverride = styleOverrides[operator];
|
@@ -90,23 +90,32 @@ module.exports = {
|
|
90
90
|
return node.type === "Line" || node.type === "Block";
|
91
91
|
}
|
92
92
|
|
93
|
+
/**
|
94
|
+
* Checks if there is padding between two tokens
|
95
|
+
* @param {Token} first The first token
|
96
|
+
* @param {Token} second The second token
|
97
|
+
* @returns {boolean} True if there is at least a line between the tokens
|
98
|
+
*/
|
99
|
+
function isPaddingBetweenTokens(first, second) {
|
100
|
+
return second.loc.start.line - first.loc.end.line >= 2;
|
101
|
+
}
|
102
|
+
|
103
|
+
|
93
104
|
/**
|
94
105
|
* Checks if the given token has a blank line after it.
|
95
106
|
* @param {Token} token The token to check.
|
96
107
|
* @returns {boolean} Whether or not the token is followed by a blank line.
|
97
108
|
*/
|
98
|
-
function
|
99
|
-
|
100
|
-
|
101
|
-
let first = token;
|
109
|
+
function getFirstBlockToken(token) {
|
110
|
+
let prev = token,
|
111
|
+
first = token;
|
102
112
|
|
103
113
|
do {
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
const firstLine = first.loc.start.line;
|
114
|
+
prev = first;
|
115
|
+
first = sourceCode.getTokenAfter(first, { includeComments: true });
|
116
|
+
} while (isComment(first) && first.loc.start.line === prev.loc.end.line);
|
108
117
|
|
109
|
-
return
|
118
|
+
return first;
|
110
119
|
}
|
111
120
|
|
112
121
|
/**
|
@@ -114,18 +123,16 @@ module.exports = {
|
|
114
123
|
* @param {Token} token The token to check
|
115
124
|
* @returns {boolean} Whether or not the token is preceeded by a blank line
|
116
125
|
*/
|
117
|
-
function
|
118
|
-
|
119
|
-
|
120
|
-
let last = token;
|
126
|
+
function getLastBlockToken(token) {
|
127
|
+
let last = token,
|
128
|
+
next = token;
|
121
129
|
|
122
130
|
do {
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
const lastLine = last.loc.end.line;
|
131
|
+
next = last;
|
132
|
+
last = sourceCode.getTokenBefore(last, { includeComments: true });
|
133
|
+
} while (isComment(last) && last.loc.end.line === next.loc.start.line);
|
127
134
|
|
128
|
-
return
|
135
|
+
return last;
|
129
136
|
}
|
130
137
|
|
131
138
|
/**
|
@@ -155,17 +162,21 @@ module.exports = {
|
|
155
162
|
*/
|
156
163
|
function checkPadding(node) {
|
157
164
|
const openBrace = getOpenBrace(node),
|
165
|
+
firstBlockToken = getFirstBlockToken(openBrace),
|
166
|
+
tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),
|
158
167
|
closeBrace = sourceCode.getLastToken(node),
|
159
|
-
|
160
|
-
|
168
|
+
lastBlockToken = getLastBlockToken(closeBrace),
|
169
|
+
tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),
|
170
|
+
blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
|
171
|
+
blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
|
161
172
|
|
162
173
|
if (requirePaddingFor(node)) {
|
163
174
|
if (!blockHasTopPadding) {
|
164
175
|
context.report({
|
165
176
|
node,
|
166
|
-
loc: { line:
|
177
|
+
loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
|
167
178
|
fix(fixer) {
|
168
|
-
return fixer.insertTextAfter(
|
179
|
+
return fixer.insertTextAfter(tokenBeforeFirst, "\n");
|
169
180
|
},
|
170
181
|
message: ALWAYS_MESSAGE
|
171
182
|
});
|
@@ -173,36 +184,34 @@ module.exports = {
|
|
173
184
|
if (!blockHasBottomPadding) {
|
174
185
|
context.report({
|
175
186
|
node,
|
176
|
-
loc: { line:
|
187
|
+
loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
|
177
188
|
fix(fixer) {
|
178
|
-
return fixer.insertTextBefore(
|
189
|
+
return fixer.insertTextBefore(tokenAfterLast, "\n");
|
179
190
|
},
|
180
191
|
message: ALWAYS_MESSAGE
|
181
192
|
});
|
182
193
|
}
|
183
194
|
} else {
|
184
195
|
if (blockHasTopPadding) {
|
185
|
-
const nextToken = sourceCode.getTokenOrCommentAfter(openBrace);
|
186
196
|
|
187
197
|
context.report({
|
188
198
|
node,
|
189
|
-
loc: { line:
|
199
|
+
loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
|
190
200
|
fix(fixer) {
|
191
|
-
return fixer.replaceTextRange([
|
201
|
+
return fixer.replaceTextRange([tokenBeforeFirst.end, firstBlockToken.start - firstBlockToken.loc.start.column], "\n");
|
192
202
|
},
|
193
203
|
message: NEVER_MESSAGE
|
194
204
|
});
|
195
205
|
}
|
196
206
|
|
197
207
|
if (blockHasBottomPadding) {
|
198
|
-
const previousToken = sourceCode.getTokenOrCommentBefore(closeBrace);
|
199
208
|
|
200
209
|
context.report({
|
201
210
|
node,
|
202
|
-
loc: { line:
|
211
|
+
loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
|
203
212
|
message: NEVER_MESSAGE,
|
204
213
|
fix(fixer) {
|
205
|
-
return fixer.replaceTextRange([
|
214
|
+
return fixer.replaceTextRange([lastBlockToken.end, tokenAfterLast.start - tokenAfterLast.loc.start.column], "\n");
|
206
215
|
}
|
207
216
|
});
|
208
217
|
}
|
@@ -89,7 +89,7 @@ module.exports = {
|
|
89
89
|
* @returns {void}
|
90
90
|
*/
|
91
91
|
function report(reportNode, type) {
|
92
|
-
context.report({ node: reportNode, message:
|
92
|
+
context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
|
93
93
|
}
|
94
94
|
|
95
95
|
/**
|
@@ -108,7 +108,7 @@ module.exports = {
|
|
108
108
|
return null;
|
109
109
|
}
|
110
110
|
|
111
|
-
const propertyDot = sourceCode.
|
111
|
+
const propertyDot = sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === ".");
|
112
112
|
|
113
113
|
return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`);
|
114
114
|
}
|
@@ -157,7 +157,7 @@ module.exports = {
|
|
157
157
|
}
|
158
158
|
|
159
159
|
if (isConcatenation(currentNode) && hasStringLiteral(currentNode) && hasNonStringLiteral(currentNode)) {
|
160
|
-
const plusSign = sourceCode.
|
160
|
+
const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
|
161
161
|
const textBeforePlus = getTextBetween(currentNode.left, plusSign);
|
162
162
|
const textAfterPlus = getTextBetween(plusSign, currentNode.right);
|
163
163
|
const leftEndsWithCurly = endsWithTemplateCurly(currentNode.left);
|
package/lib/rules/quotes.js
CHANGED
@@ -33,6 +33,9 @@ const QUOTE_SETTINGS = {
|
|
33
33
|
}
|
34
34
|
};
|
35
35
|
|
36
|
+
// An unescaped newline is a newline preceded by an even number of backslashes.
|
37
|
+
const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`);
|
38
|
+
|
36
39
|
/**
|
37
40
|
* Switches quoting of javascript string between ' " and `
|
38
41
|
* escaping and unescaping as necessary.
|
@@ -254,15 +257,16 @@ module.exports = {
|
|
254
257
|
TemplateLiteral(node) {
|
255
258
|
|
256
259
|
// If backticks are expected or it's a tagged template, then this shouldn't throw an errors
|
257
|
-
if (
|
260
|
+
if (
|
261
|
+
allowTemplateLiterals ||
|
262
|
+
quoteOption === "backtick" ||
|
263
|
+
node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi
|
264
|
+
) {
|
258
265
|
return;
|
259
266
|
}
|
260
267
|
|
261
|
-
|
262
|
-
|
263
|
-
* An unescaped newline is a newline preceded by an even number of backslashes.
|
264
|
-
*/
|
265
|
-
const shouldWarn = node.quasis.length === 1 && !/(^|[^\\])(\\\\)*[\r\n\u2028\u2029]/.test(node.quasis[0].value.raw);
|
268
|
+
// A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
|
269
|
+
const shouldWarn = node.quasis.length === 1 && !UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw);
|
266
270
|
|
267
271
|
if (shouldWarn) {
|
268
272
|
context.report({
|
@@ -206,6 +206,10 @@ module.exports = {
|
|
206
206
|
DebuggerStatement: checkNode,
|
207
207
|
ReturnStatement: checkNode,
|
208
208
|
ThrowStatement: checkNode,
|
209
|
+
ImportDeclaration: checkNode,
|
210
|
+
ExportNamedDeclaration: checkNode,
|
211
|
+
ExportAllDeclaration: checkNode,
|
212
|
+
ExportDefaultDeclaration: checkNode,
|
209
213
|
ForStatement(node) {
|
210
214
|
if (node.init) {
|
211
215
|
checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
|
package/lib/rules/semi.js
CHANGED
@@ -4,6 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const FixTracker = require("../util/fix-tracker");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Rule Definition
|
9
15
|
//------------------------------------------------------------------------------
|
@@ -85,7 +91,13 @@ module.exports = {
|
|
85
91
|
message = "Extra semicolon.";
|
86
92
|
loc = loc.start;
|
87
93
|
fix = function(fixer) {
|
88
|
-
|
94
|
+
|
95
|
+
// Expand the replacement range to include the surrounding
|
96
|
+
// tokens to avoid conflicting with no-extra-semi.
|
97
|
+
// https://github.com/eslint/eslint/issues/7928
|
98
|
+
return new FixTracker(fixer, sourceCode)
|
99
|
+
.retainSurroundingTokens(lastToken)
|
100
|
+
.remove(lastToken);
|
89
101
|
};
|
90
102
|
}
|
91
103
|
|
@@ -4,6 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const astUtils = require("../ast-utils");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Rule Definition
|
9
15
|
//------------------------------------------------------------------------------
|
@@ -104,7 +110,7 @@ module.exports = {
|
|
104
110
|
const isAnonymousGenerator = node.generator && !isNamed;
|
105
111
|
const isNormalArrow = isArrow && !node.async;
|
106
112
|
const isArrowWithoutParens = isArrow && sourceCode.getFirstToken(node, 1).value !== "(";
|
107
|
-
let forbidSpacing, requireSpacing
|
113
|
+
let forbidSpacing, requireSpacing;
|
108
114
|
|
109
115
|
// isAnonymousGenerator → `generator-star-spacing` should warn it. E.g. `function* () {}`
|
110
116
|
// isNormalArrow → ignore always.
|
@@ -124,10 +130,7 @@ module.exports = {
|
|
124
130
|
requireSpacing = requireAnonymousFunctionSpacing;
|
125
131
|
}
|
126
132
|
|
127
|
-
rightToken = sourceCode.getFirstToken(node);
|
128
|
-
while (rightToken.value !== "(") {
|
129
|
-
rightToken = sourceCode.getTokenAfter(rightToken);
|
130
|
-
}
|
133
|
+
const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
|
131
134
|
const leftToken = sourceCode.getTokenBefore(rightToken);
|
132
135
|
const location = leftToken.loc.end;
|
133
136
|
|
@@ -68,6 +68,21 @@ module.exports = {
|
|
68
68
|
return node.argument && node.argument.type && node.argument.type === "ObjectExpression";
|
69
69
|
}
|
70
70
|
|
71
|
+
/**
|
72
|
+
* Check if it is safe to remove the spaces between the two tokens in
|
73
|
+
* the context of a non-word prefix unary operator. For example, `+ +1`
|
74
|
+
* cannot safely be changed to `++1`.
|
75
|
+
* @param {Token} firstToken The operator for a non-word prefix unary operator
|
76
|
+
* @param {Token} secondToken The first token of its operand
|
77
|
+
* @returns {boolean} Whether or not the spacing between the tokens can be removed
|
78
|
+
*/
|
79
|
+
function canRemoveSpacesBetween(firstToken, secondToken) {
|
80
|
+
return !(
|
81
|
+
(firstToken.value === "+" && secondToken.value[0] === "+") ||
|
82
|
+
(firstToken.value === "-" && secondToken.value[0] === "-")
|
83
|
+
);
|
84
|
+
}
|
85
|
+
|
71
86
|
/**
|
72
87
|
* Checks if an override exists for a given operator.
|
73
88
|
* @param {ASTnode} node AST node
|
@@ -244,7 +259,10 @@ module.exports = {
|
|
244
259
|
operator: firstToken.value
|
245
260
|
},
|
246
261
|
fix(fixer) {
|
247
|
-
|
262
|
+
if (canRemoveSpacesBetween(firstToken, secondToken)) {
|
263
|
+
return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
|
264
|
+
}
|
265
|
+
return null;
|
248
266
|
}
|
249
267
|
});
|
250
268
|
}
|
@@ -5,6 +5,7 @@
|
|
5
5
|
"use strict";
|
6
6
|
|
7
7
|
const lodash = require("lodash");
|
8
|
+
const astUtils = require("../ast-utils");
|
8
9
|
|
9
10
|
//------------------------------------------------------------------------------
|
10
11
|
// Helpers
|
@@ -88,8 +89,7 @@ function createExceptionsPattern(exceptions) {
|
|
88
89
|
pattern += exceptions.map(escapeAndRepeat).join("|");
|
89
90
|
pattern += ")";
|
90
91
|
}
|
91
|
-
|
92
|
-
pattern += "(?:$|[\n\r]))";
|
92
|
+
pattern += `(?:$|[${Array.from(astUtils.LINEBREAKS).join("")}]))`;
|
93
93
|
}
|
94
94
|
|
95
95
|
return pattern;
|
package/lib/rules/strict.js
CHANGED
@@ -9,6 +9,8 @@
|
|
9
9
|
// Requirements
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
+
const astUtils = require("../ast-utils");
|
13
|
+
|
12
14
|
//------------------------------------------------------------------------------
|
13
15
|
// Helpers
|
14
16
|
//------------------------------------------------------------------------------
|
@@ -23,7 +25,7 @@ const messages = {
|
|
23
25
|
implied: "'use strict' is unnecessary when implied strict mode is enabled.",
|
24
26
|
unnecessaryInClasses: "'use strict' is unnecessary inside of classes.",
|
25
27
|
nonSimpleParameterList: "'use strict' directive inside a function with non-simple parameter list throws a syntax error since ES2016.",
|
26
|
-
wrap: "Wrap
|
28
|
+
wrap: "Wrap {{name}} in a function with 'use strict' directive."
|
27
29
|
};
|
28
30
|
|
29
31
|
/**
|
@@ -188,7 +190,11 @@ module.exports = {
|
|
188
190
|
if (isSimpleParameterList(node.params)) {
|
189
191
|
context.report({ node, message: messages.function });
|
190
192
|
} else {
|
191
|
-
context.report({
|
193
|
+
context.report({
|
194
|
+
node,
|
195
|
+
message: messages.wrap,
|
196
|
+
data: { name: astUtils.getFunctionNameWithKind(node) }
|
197
|
+
});
|
192
198
|
}
|
193
199
|
}
|
194
200
|
|
@@ -212,8 +218,8 @@ module.exports = {
|
|
212
218
|
*/
|
213
219
|
function enterFunction(node) {
|
214
220
|
const isBlock = node.body.type === "BlockStatement",
|
215
|
-
useStrictDirectives = isBlock
|
216
|
-
getUseStrictDirectives(node.body.body) : [];
|
221
|
+
useStrictDirectives = isBlock
|
222
|
+
? getUseStrictDirectives(node.body.body) : [];
|
217
223
|
|
218
224
|
if (mode === "function") {
|
219
225
|
enterFunctionInFunctionMode(node, useStrictDirectives);
|
package/lib/rules/unicode-bom.js
CHANGED
@@ -45,7 +45,7 @@ module.exports = {
|
|
45
45
|
loc: location,
|
46
46
|
message: "Expected Unicode BOM (Byte Order Mark).",
|
47
47
|
fix(fixer) {
|
48
|
-
return fixer.
|
48
|
+
return fixer.insertTextBeforeRange([0, 1], "\uFEFF");
|
49
49
|
}
|
50
50
|
});
|
51
51
|
} else if (sourceCode.hasBOM && (requireBOM === "never")) {
|
package/lib/rules/wrap-iife.js
CHANGED
@@ -5,6 +5,10 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
8
12
|
const astUtils = require("../ast-utils");
|
9
13
|
|
10
14
|
//------------------------------------------------------------------------------
|
@@ -51,11 +55,7 @@ module.exports = {
|
|
51
55
|
* @private
|
52
56
|
*/
|
53
57
|
function wrapped(node) {
|
54
|
-
|
55
|
-
nextToken = sourceCode.getTokenAfter(node);
|
56
|
-
|
57
|
-
return previousToken && previousToken.value === "(" &&
|
58
|
-
nextToken && nextToken.value === ")";
|
58
|
+
return astUtils.isParenthesised(sourceCode, node);
|
59
59
|
}
|
60
60
|
|
61
61
|
/**
|
package/lib/rules/yoda.js
CHANGED
@@ -235,12 +235,7 @@ module.exports = {
|
|
235
235
|
* paren token.
|
236
236
|
*/
|
237
237
|
function isParenWrapped() {
|
238
|
-
|
239
|
-
|
240
|
-
return ((tokenBefore = sourceCode.getTokenBefore(node)) &&
|
241
|
-
tokenBefore.value === "(" &&
|
242
|
-
(tokenAfter = sourceCode.getTokenAfter(node)) &&
|
243
|
-
tokenAfter.value === ")");
|
238
|
+
return astUtils.isParenthesised(sourceCode, node);
|
244
239
|
}
|
245
240
|
|
246
241
|
return (node.type === "LogicalExpression" &&
|
@@ -269,11 +264,11 @@ module.exports = {
|
|
269
264
|
* @returns {string} A string representation of the node with the sides and operator flipped
|
270
265
|
*/
|
271
266
|
function getFlippedString(node) {
|
272
|
-
const operatorToken = sourceCode.
|
267
|
+
const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
|
273
268
|
const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]);
|
274
269
|
const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]);
|
275
|
-
const leftText = sourceCode.getText().slice(
|
276
|
-
const rightText = sourceCode.getText().slice(sourceCode.getTokenAfter(operatorToken).range[0],
|
270
|
+
const leftText = sourceCode.getText().slice(node.range[0], sourceCode.getTokenBefore(operatorToken).range[1]);
|
271
|
+
const rightText = sourceCode.getText().slice(sourceCode.getTokenAfter(operatorToken).range[0], node.range[1]);
|
277
272
|
|
278
273
|
return rightText + textBeforeOperator + OPERATOR_FLIP_MAP[operatorToken.value] + textAfterOperator + leftText;
|
279
274
|
}
|
@@ -147,6 +147,12 @@ function RuleTester(testerConfig) {
|
|
147
147
|
lodash.cloneDeep(defaultConfig),
|
148
148
|
testerConfig
|
149
149
|
);
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Rule definitions to define before tests.
|
153
|
+
* @type {Object}
|
154
|
+
*/
|
155
|
+
this.rules = {};
|
150
156
|
}
|
151
157
|
|
152
158
|
/**
|
@@ -239,7 +245,7 @@ RuleTester.prototype = {
|
|
239
245
|
* @returns {void}
|
240
246
|
*/
|
241
247
|
defineRule(name, rule) {
|
242
|
-
|
248
|
+
this.rules[name] = rule;
|
243
249
|
},
|
244
250
|
|
245
251
|
/**
|
@@ -419,6 +425,28 @@ RuleTester.prototype = {
|
|
419
425
|
assertASTDidntChange(result.beforeAST, result.afterAST);
|
420
426
|
}
|
421
427
|
|
428
|
+
/**
|
429
|
+
* Asserts that the message matches its expected value. If the expected
|
430
|
+
* value is a regular expression, it is checked against the actual
|
431
|
+
* value.
|
432
|
+
* @param {string} actual Actual value
|
433
|
+
* @param {string|RegExp} expected Expected value
|
434
|
+
* @returns {void}
|
435
|
+
* @private
|
436
|
+
*/
|
437
|
+
function assertMessageMatches(actual, expected) {
|
438
|
+
if (expected instanceof RegExp) {
|
439
|
+
|
440
|
+
// assert.js doesn't have a built-in RegExp match function
|
441
|
+
assert.ok(
|
442
|
+
expected.test(actual),
|
443
|
+
`Expected '${actual}' to match ${expected}`
|
444
|
+
);
|
445
|
+
} else {
|
446
|
+
assert.equal(actual, expected);
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
422
450
|
/**
|
423
451
|
* Check if the template is invalid or not
|
424
452
|
* all invalid cases go through this.
|
@@ -448,10 +476,10 @@ RuleTester.prototype = {
|
|
448
476
|
assert.ok(!("fatal" in messages[i]), `A fatal parsing error occurred: ${messages[i].message}`);
|
449
477
|
assert.equal(messages[i].ruleId, ruleName, "Error rule name should be the same as the name of the rule being tested");
|
450
478
|
|
451
|
-
if (typeof item.errors[i] === "string") {
|
479
|
+
if (typeof item.errors[i] === "string" || item.errors[i] instanceof RegExp) {
|
452
480
|
|
453
481
|
// Just an error message.
|
454
|
-
|
482
|
+
assertMessageMatches(messages[i].message, item.errors[i]);
|
455
483
|
} else if (typeof item.errors[i] === "object") {
|
456
484
|
|
457
485
|
/*
|
@@ -460,7 +488,7 @@ RuleTester.prototype = {
|
|
460
488
|
* column.
|
461
489
|
*/
|
462
490
|
if (item.errors[i].message) {
|
463
|
-
|
491
|
+
assertMessageMatches(messages[i].message, item.errors[i].message);
|
464
492
|
}
|
465
493
|
|
466
494
|
if (item.errors[i].type) {
|
@@ -484,17 +512,24 @@ RuleTester.prototype = {
|
|
484
512
|
}
|
485
513
|
} else {
|
486
514
|
|
487
|
-
//
|
488
|
-
assert.fail(messages[i], null, "Error should be a string or
|
515
|
+
// Message was an unexpected type
|
516
|
+
assert.fail(messages[i], null, "Error should be a string, object, or RegExp.");
|
489
517
|
}
|
490
518
|
}
|
519
|
+
}
|
491
520
|
|
492
|
-
|
521
|
+
if (item.hasOwnProperty("output")) {
|
522
|
+
if (item.output === null) {
|
523
|
+
assert.strictEqual(
|
524
|
+
messages.filter(message => message.fix).length,
|
525
|
+
0,
|
526
|
+
"Expected no autofixes to be suggested"
|
527
|
+
);
|
528
|
+
} else {
|
493
529
|
const fixResult = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
|
494
530
|
|
495
531
|
assert.equal(fixResult.output, item.output, "Output is incorrect.");
|
496
532
|
}
|
497
|
-
|
498
533
|
}
|
499
534
|
|
500
535
|
assertASTDidntChange(result.beforeAST, result.afterAST);
|
@@ -507,7 +542,8 @@ RuleTester.prototype = {
|
|
507
542
|
RuleTester.describe(ruleName, () => {
|
508
543
|
RuleTester.describe("valid", () => {
|
509
544
|
test.valid.forEach(valid => {
|
510
|
-
RuleTester.it(valid.code
|
545
|
+
RuleTester.it(typeof valid === "object" ? valid.code : valid, () => {
|
546
|
+
eslint.defineRules(this.rules);
|
511
547
|
testValidTemplate(ruleName, valid);
|
512
548
|
});
|
513
549
|
});
|
@@ -516,6 +552,7 @@ RuleTester.prototype = {
|
|
516
552
|
RuleTester.describe("invalid", () => {
|
517
553
|
test.invalid.forEach(invalid => {
|
518
554
|
RuleTester.it(invalid.code, () => {
|
555
|
+
eslint.defineRules(this.rules);
|
519
556
|
testInvalidTemplate(ruleName, invalid);
|
520
557
|
});
|
521
558
|
});
|