eslint 4.7.1 → 4.10.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 +113 -0
- package/README.md +34 -19
- package/conf/default-cli-options.js +2 -1
- package/conf/eslint-recommended.js +2 -0
- package/lib/ast-utils.js +2 -1
- package/lib/cli-engine.js +26 -5
- package/lib/cli.js +17 -9
- package/lib/code-path-analysis/code-path-segment.js +39 -39
- package/lib/code-path-analysis/code-path-state.js +3 -0
- package/lib/formatters/html-template-message.html +1 -1
- package/lib/formatters/html-template-page.html +3 -1
- package/lib/formatters/html.js +2 -1
- package/lib/ignored-paths.js +1 -1
- package/lib/linter.js +43 -71
- package/lib/logging.js +2 -2
- package/lib/options.js +12 -0
- package/lib/rules/array-bracket-newline.js +19 -5
- package/lib/rules/block-spacing.js +1 -1
- package/lib/rules/callback-return.js +2 -1
- package/lib/rules/capitalized-comments.js +2 -1
- package/lib/rules/comma-style.js +3 -1
- package/lib/rules/dot-notation.js +56 -35
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/indent-legacy.js +5 -2
- package/lib/rules/indent.js +25 -19
- package/lib/rules/lines-around-comment.js +33 -4
- package/lib/rules/lines-between-class-members.js +91 -0
- package/lib/rules/max-len.js +2 -3
- package/lib/rules/multiline-comment-style.js +294 -0
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/newline-before-return.js +4 -2
- package/lib/rules/no-alert.js +7 -15
- package/lib/rules/no-catch-shadow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -2
- package/lib/rules/no-control-regex.js +2 -1
- package/lib/rules/no-else-return.js +43 -8
- package/lib/rules/no-extra-parens.js +6 -3
- package/lib/rules/no-lonely-if.js +2 -1
- package/lib/rules/no-loop-func.js +2 -3
- package/lib/rules/no-mixed-requires.js +8 -4
- package/lib/rules/no-restricted-imports.js +86 -17
- package/lib/rules/no-restricted-modules.js +84 -15
- package/lib/rules/no-trailing-spaces.js +1 -1
- package/lib/rules/no-unneeded-ternary.js +3 -1
- package/lib/rules/no-unused-labels.js +2 -1
- package/lib/rules/no-useless-computed-key.js +2 -1
- package/lib/rules/no-useless-escape.js +8 -1
- package/lib/rules/no-var.js +11 -0
- package/lib/rules/object-shorthand.js +6 -2
- package/lib/rules/operator-linebreak.js +3 -1
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/require-jsdoc.js +11 -18
- package/lib/rules/sort-imports.js +6 -3
- package/lib/rules/space-unary-ops.js +6 -8
- package/lib/rules/valid-jsdoc.js +39 -33
- package/lib/testers/rule-tester.js +20 -6
- package/lib/util/apply-disable-directives.js +56 -27
- package/lib/util/node-event-generator.js +6 -20
- package/lib/util/safe-emitter.js +54 -0
- package/lib/util/source-code.js +73 -67
- package/messages/no-config-found.txt +1 -1
- package/package.json +3 -4
- package/lib/internal-rules/.eslintrc.yml +0 -3
- package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
- package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
package/lib/rules/indent.js
CHANGED
@@ -235,10 +235,12 @@ class OffsetStorage {
|
|
235
235
|
/**
|
236
236
|
* @param {TokenInfo} tokenInfo a TokenInfo instance
|
237
237
|
* @param {number} indentSize The desired size of each indentation level
|
238
|
+
* @param {string} indentType The indentation character
|
238
239
|
*/
|
239
|
-
constructor(tokenInfo, indentSize) {
|
240
|
+
constructor(tokenInfo, indentSize, indentType) {
|
240
241
|
this._tokenInfo = tokenInfo;
|
241
242
|
this._indentSize = indentSize;
|
243
|
+
this._indentType = indentType;
|
242
244
|
|
243
245
|
this._tree = new BinarySearchTree();
|
244
246
|
this._tree.insert(0, { offset: 0, from: null, force: false });
|
@@ -408,7 +410,7 @@ class OffsetStorage {
|
|
408
410
|
/**
|
409
411
|
* Gets the desired indent of a token
|
410
412
|
* @param {Token} token The token
|
411
|
-
* @returns {
|
413
|
+
* @returns {string} The desired indent of the token
|
412
414
|
*/
|
413
415
|
getDesiredIndent(token) {
|
414
416
|
if (!this._desiredIndentCache.has(token)) {
|
@@ -417,7 +419,10 @@ class OffsetStorage {
|
|
417
419
|
|
418
420
|
// If the token is ignored, use the actual indent of the token as the desired indent.
|
419
421
|
// This ensures that no errors are reported for this token.
|
420
|
-
this._desiredIndentCache.set(
|
422
|
+
this._desiredIndentCache.set(
|
423
|
+
token,
|
424
|
+
this._tokenInfo.getTokenIndent(token)
|
425
|
+
);
|
421
426
|
} else if (this._lockedFirstTokens.has(token)) {
|
422
427
|
const firstToken = this._lockedFirstTokens.get(token);
|
423
428
|
|
@@ -428,7 +433,7 @@ class OffsetStorage {
|
|
428
433
|
this.getDesiredIndent(this._tokenInfo.getFirstTokenOfLine(firstToken)) +
|
429
434
|
|
430
435
|
// (space between the start of the first element's line and the first element)
|
431
|
-
(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column)
|
436
|
+
this._indentType.repeat(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column)
|
432
437
|
);
|
433
438
|
} else {
|
434
439
|
const offsetInfo = this._getOffsetDescriptor(token);
|
@@ -436,9 +441,12 @@ class OffsetStorage {
|
|
436
441
|
offsetInfo.from &&
|
437
442
|
offsetInfo.from.loc.start.line === token.loc.start.line &&
|
438
443
|
!offsetInfo.force
|
439
|
-
) ? 0 : offsetInfo.offset;
|
444
|
+
) ? 0 : offsetInfo.offset * this._indentSize;
|
440
445
|
|
441
|
-
this._desiredIndentCache.set(
|
446
|
+
this._desiredIndentCache.set(
|
447
|
+
token,
|
448
|
+
(offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : "") + this._indentType.repeat(offset)
|
449
|
+
);
|
442
450
|
}
|
443
451
|
}
|
444
452
|
return this._desiredIndentCache.get(token);
|
@@ -655,7 +663,7 @@ module.exports = {
|
|
655
663
|
|
656
664
|
const sourceCode = context.getSourceCode();
|
657
665
|
const tokenInfo = new TokenInfo(sourceCode);
|
658
|
-
const offsets = new OffsetStorage(tokenInfo, indentSize);
|
666
|
+
const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t");
|
659
667
|
const parameterParens = new WeakSet();
|
660
668
|
|
661
669
|
/**
|
@@ -688,27 +696,24 @@ module.exports = {
|
|
688
696
|
/**
|
689
697
|
* Reports a given indent violation
|
690
698
|
* @param {Token} token Token violating the indent rule
|
691
|
-
* @param {
|
692
|
-
* @param {int} gottenSpaces Actual number of indentation spaces for the token
|
693
|
-
* @param {int} gottenTabs Actual number of indentation tabs for the token
|
699
|
+
* @param {string} neededIndent Expected indentation string
|
694
700
|
* @returns {void}
|
695
701
|
*/
|
696
|
-
function report(token,
|
702
|
+
function report(token, neededIndent) {
|
697
703
|
const actualIndent = Array.from(tokenInfo.getTokenIndent(token));
|
698
704
|
const numSpaces = actualIndent.filter(char => char === " ").length;
|
699
705
|
const numTabs = actualIndent.filter(char => char === "\t").length;
|
700
|
-
const neededChars = neededIndentLevel * indentSize;
|
701
706
|
|
702
707
|
context.report({
|
703
708
|
node: token,
|
704
|
-
message: createErrorMessage(
|
709
|
+
message: createErrorMessage(neededIndent.length, numSpaces, numTabs),
|
705
710
|
loc: {
|
706
711
|
start: { line: token.loc.start.line, column: 0 },
|
707
712
|
end: { line: token.loc.start.line, column: token.loc.start.column }
|
708
713
|
},
|
709
714
|
fix(fixer) {
|
710
715
|
const range = [token.range[0] - token.loc.start.column, token.range[0]];
|
711
|
-
const newText =
|
716
|
+
const newText = neededIndent;
|
712
717
|
|
713
718
|
return fixer.replaceTextRange(range, newText);
|
714
719
|
}
|
@@ -718,14 +723,13 @@ module.exports = {
|
|
718
723
|
/**
|
719
724
|
* Checks if a token's indentation is correct
|
720
725
|
* @param {Token} token Token to examine
|
721
|
-
* @param {
|
726
|
+
* @param {string} desiredIndent Desired indentation of the string
|
722
727
|
* @returns {boolean} `true` if the token's indentation is correct
|
723
728
|
*/
|
724
|
-
function validateTokenIndent(token,
|
729
|
+
function validateTokenIndent(token, desiredIndent) {
|
725
730
|
const indentation = tokenInfo.getTokenIndent(token);
|
726
|
-
const expectedChar = indentType === "space" ? " " : "\t";
|
727
731
|
|
728
|
-
return indentation ===
|
732
|
+
return indentation === desiredIndent ||
|
729
733
|
|
730
734
|
// To avoid conflicts with no-mixed-spaces-and-tabs, don't report mixed spaces and tabs.
|
731
735
|
indentation.includes(" ") && indentation.includes("\t");
|
@@ -1241,7 +1245,9 @@ module.exports = {
|
|
1241
1245
|
NewExpression(node) {
|
1242
1246
|
|
1243
1247
|
// Only indent the arguments if the NewExpression has parens (e.g. `new Foo(bar)` or `new Foo()`, but not `new Foo`
|
1244
|
-
if (node.arguments.length > 0 ||
|
1248
|
+
if (node.arguments.length > 0 ||
|
1249
|
+
astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
|
1250
|
+
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, 1))) {
|
1245
1251
|
addFunctionCallIndent(node);
|
1246
1252
|
}
|
1247
1253
|
},
|
@@ -82,6 +82,12 @@ module.exports = {
|
|
82
82
|
allowBlockEnd: {
|
83
83
|
type: "boolean"
|
84
84
|
},
|
85
|
+
allowClassStart: {
|
86
|
+
type: "boolean"
|
87
|
+
},
|
88
|
+
allowClassEnd: {
|
89
|
+
type: "boolean"
|
90
|
+
},
|
85
91
|
allowObjectStart: {
|
86
92
|
type: "boolean"
|
87
93
|
},
|
@@ -224,6 +230,24 @@ module.exports = {
|
|
224
230
|
return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
|
225
231
|
}
|
226
232
|
|
233
|
+
/**
|
234
|
+
* Returns whether or not comments are at the class start or not.
|
235
|
+
* @param {token} token The Comment token.
|
236
|
+
* @returns {boolean} True if the comment is at class start.
|
237
|
+
*/
|
238
|
+
function isCommentAtClassStart(token) {
|
239
|
+
return isCommentAtParentStart(token, "ClassBody");
|
240
|
+
}
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Returns whether or not comments are at the class end or not.
|
244
|
+
* @param {token} token The Comment token.
|
245
|
+
* @returns {boolean} True if the comment is at class end.
|
246
|
+
*/
|
247
|
+
function isCommentAtClassEnd(token) {
|
248
|
+
return isCommentAtParentEnd(token, "ClassBody");
|
249
|
+
}
|
250
|
+
|
227
251
|
/**
|
228
252
|
* Returns whether or not comments are at the object start or not.
|
229
253
|
* @param {token} token The Comment token.
|
@@ -284,15 +308,20 @@ module.exports = {
|
|
284
308
|
nextLineNum = token.loc.end.line + 1,
|
285
309
|
commentIsNotAlone = codeAroundComment(token);
|
286
310
|
|
287
|
-
const blockStartAllowed = options.allowBlockStart &&
|
288
|
-
|
311
|
+
const blockStartAllowed = options.allowBlockStart &&
|
312
|
+
isCommentAtBlockStart(token) &&
|
313
|
+
!(options.allowClassStart === false &&
|
314
|
+
isCommentAtClassStart(token)),
|
315
|
+
blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
|
316
|
+
classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
|
317
|
+
classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
|
289
318
|
objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
|
290
319
|
objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
|
291
320
|
arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
|
292
321
|
arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
|
293
322
|
|
294
|
-
const exceptionStartAllowed = blockStartAllowed || objectStartAllowed || arrayStartAllowed;
|
295
|
-
const exceptionEndAllowed = blockEndAllowed || objectEndAllowed || arrayEndAllowed;
|
323
|
+
const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
|
324
|
+
const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
|
296
325
|
|
297
326
|
// ignore top of the file and bottom of the file
|
298
327
|
if (prevLineNum < 1) {
|
@@ -0,0 +1,91 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to check empty newline between class members
|
3
|
+
* @author 薛定谔的猫<hh_2013@foxmail.com>
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
const astUtils = require("../ast-utils");
|
8
|
+
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Rule Definition
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
module.exports = {
|
14
|
+
meta: {
|
15
|
+
docs: {
|
16
|
+
description: "require or disallow an empty line between class members",
|
17
|
+
category: "Stylistic Issues",
|
18
|
+
recommended: false
|
19
|
+
},
|
20
|
+
|
21
|
+
fixable: "whitespace",
|
22
|
+
|
23
|
+
schema: [
|
24
|
+
{
|
25
|
+
enum: ["always", "never"]
|
26
|
+
},
|
27
|
+
{
|
28
|
+
type: "object",
|
29
|
+
properties: {
|
30
|
+
exceptAfterSingleLine: {
|
31
|
+
type: "boolean"
|
32
|
+
}
|
33
|
+
},
|
34
|
+
additionalProperties: false
|
35
|
+
}
|
36
|
+
]
|
37
|
+
},
|
38
|
+
|
39
|
+
create(context) {
|
40
|
+
|
41
|
+
const options = [];
|
42
|
+
|
43
|
+
options[0] = context.options[0] || "always";
|
44
|
+
options[1] = context.options[1] || { exceptAfterSingleLine: false };
|
45
|
+
|
46
|
+
const ALWAYS_MESSAGE = "Expected blank line between class members.";
|
47
|
+
const NEVER_MESSAGE = "Unexpected blank line between class members.";
|
48
|
+
|
49
|
+
const sourceCode = context.getSourceCode();
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Checks if there is padding between two tokens
|
53
|
+
* @param {Token} first The first token
|
54
|
+
* @param {Token} second The second token
|
55
|
+
* @returns {boolean} True if there is at least a line between the tokens
|
56
|
+
*/
|
57
|
+
function isPaddingBetweenTokens(first, second) {
|
58
|
+
return second.loc.start.line - first.loc.end.line >= 2;
|
59
|
+
}
|
60
|
+
|
61
|
+
return {
|
62
|
+
ClassBody(node) {
|
63
|
+
const body = node.body;
|
64
|
+
|
65
|
+
for (let i = 0; i < body.length - 1; i++) {
|
66
|
+
const curFirst = sourceCode.getFirstToken(body[i]);
|
67
|
+
const curLast = sourceCode.getLastToken(body[i]);
|
68
|
+
const comments = sourceCode.getCommentsBefore(body[i + 1]);
|
69
|
+
const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]);
|
70
|
+
const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
|
71
|
+
const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
|
72
|
+
const skip = !isMulti && options[1].exceptAfterSingleLine;
|
73
|
+
|
74
|
+
|
75
|
+
if ((options[0] === "always" && !skip && !isPadded) ||
|
76
|
+
(options[0] === "never" && isPadded)) {
|
77
|
+
context.report({
|
78
|
+
node: body[i + 1],
|
79
|
+
message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE,
|
80
|
+
fix(fixer) {
|
81
|
+
return isPadded
|
82
|
+
? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
|
83
|
+
: fixer.insertTextAfter(curLast, "\n");
|
84
|
+
}
|
85
|
+
});
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
};
|
90
|
+
}
|
91
|
+
};
|
package/lib/rules/max-len.js
CHANGED
@@ -181,11 +181,10 @@ module.exports = {
|
|
181
181
|
* Gets the line after the comment and any remaining trailing whitespace is
|
182
182
|
* stripped.
|
183
183
|
* @param {string} line The source line with a trailing comment
|
184
|
-
* @param {number} lineNumber The one-indexed line number this is on
|
185
184
|
* @param {ASTNode} comment The comment to remove
|
186
185
|
* @returns {string} Line without comment and trailing whitepace
|
187
186
|
*/
|
188
|
-
function stripTrailingComment(line,
|
187
|
+
function stripTrailingComment(line, comment) {
|
189
188
|
|
190
189
|
// loc.column is zero-indexed
|
191
190
|
return line.slice(0, comment.loc.start.column).replace(/\s+$/, "");
|
@@ -306,7 +305,7 @@ module.exports = {
|
|
306
305
|
if (isFullLineComment(line, lineNumber, comment)) {
|
307
306
|
lineIsComment = true;
|
308
307
|
} else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
|
309
|
-
line = stripTrailingComment(line,
|
308
|
+
line = stripTrailingComment(line, comment);
|
310
309
|
}
|
311
310
|
}
|
312
311
|
if (ignorePattern && ignorePattern.test(line) ||
|
@@ -0,0 +1,294 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview enforce a particular style for multiline comments
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
const astUtils = require("../ast-utils");
|
8
|
+
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Rule Definition
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
module.exports = {
|
14
|
+
meta: {
|
15
|
+
docs: {
|
16
|
+
description: "enforce a particular style for multiline comments",
|
17
|
+
category: "Stylistic Issues",
|
18
|
+
recommended: false
|
19
|
+
},
|
20
|
+
fixable: "whitespace",
|
21
|
+
schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }]
|
22
|
+
},
|
23
|
+
|
24
|
+
create(context) {
|
25
|
+
const sourceCode = context.getSourceCode();
|
26
|
+
const option = context.options[0] || "starred-block";
|
27
|
+
|
28
|
+
const EXPECTED_BLOCK_ERROR = "Expected a block comment instead of consecutive line comments.";
|
29
|
+
const START_NEWLINE_ERROR = "Expected a linebreak after '/*'.";
|
30
|
+
const END_NEWLINE_ERROR = "Expected a linebreak before '*/'.";
|
31
|
+
const MISSING_STAR_ERROR = "Expected a '*' at the start of this line.";
|
32
|
+
const ALIGNMENT_ERROR = "Expected this line to be aligned with the start of the comment.";
|
33
|
+
const EXPECTED_LINES_ERROR = "Expected multiple line comments instead of a block comment.";
|
34
|
+
|
35
|
+
//----------------------------------------------------------------------
|
36
|
+
// Helpers
|
37
|
+
//----------------------------------------------------------------------
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Gets a list of comment lines in a group
|
41
|
+
* @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
|
42
|
+
* @returns {string[]} A list of comment lines
|
43
|
+
*/
|
44
|
+
function getCommentLines(commentGroup) {
|
45
|
+
if (commentGroup[0].type === "Line") {
|
46
|
+
return commentGroup.map(comment => comment.value);
|
47
|
+
}
|
48
|
+
return commentGroup[0].value
|
49
|
+
.split(astUtils.LINEBREAK_MATCHER)
|
50
|
+
.map(line => line.replace(/^\s*\*?/, ""));
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Converts a comment into starred-block form
|
55
|
+
* @param {Token} firstComment The first comment of the group being converted
|
56
|
+
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
|
57
|
+
* @returns {string} A representation of the comment value in starred-block form, excluding start and end markers
|
58
|
+
*/
|
59
|
+
function convertToStarredBlock(firstComment, commentLinesList) {
|
60
|
+
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
|
61
|
+
const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`);
|
62
|
+
|
63
|
+
return `\n${starredLines.join("\n")}\n${initialOffset} `;
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Converts a comment into separate-line form
|
68
|
+
* @param {Token} firstComment The first comment of the group being converted
|
69
|
+
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
|
70
|
+
* @returns {string} A representation of the comment value in separate-line form
|
71
|
+
*/
|
72
|
+
function convertToSeparateLines(firstComment, commentLinesList) {
|
73
|
+
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
|
74
|
+
const separateLines = commentLinesList.map(line => `// ${line.trim()}`);
|
75
|
+
|
76
|
+
return separateLines.join(`\n${initialOffset}`);
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Converts a comment into bare-block form
|
81
|
+
* @param {Token} firstComment The first comment of the group being converted
|
82
|
+
* @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
|
83
|
+
* @returns {string} A representation of the comment value in bare-block form
|
84
|
+
*/
|
85
|
+
function convertToBlock(firstComment, commentLinesList) {
|
86
|
+
const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
|
87
|
+
const blockLines = commentLinesList.map(line => line.trim());
|
88
|
+
|
89
|
+
return `/* ${blockLines.join(`\n${initialOffset} `)} */`;
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Check a comment is JSDoc form
|
94
|
+
* @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
|
95
|
+
* @returns {boolean} if commentGroup is JSDoc form, return true
|
96
|
+
*/
|
97
|
+
function isJSDoc(commentGroup) {
|
98
|
+
const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER);
|
99
|
+
|
100
|
+
return commentGroup[0].type === "Block" &&
|
101
|
+
/^\*\s*$/.test(lines[0]) &&
|
102
|
+
lines.slice(1, -1).every(line => /^\s* /.test(line)) &&
|
103
|
+
/^\s*$/.test(lines[lines.length - 1]);
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* Each method checks a group of comments to see if it's valid according to the given option.
|
108
|
+
* @param {Token[]} commentGroup A list of comments that appear together. This will either contain a single
|
109
|
+
* block comment or multiple line comments.
|
110
|
+
* @returns {void}
|
111
|
+
*/
|
112
|
+
const commentGroupCheckers = {
|
113
|
+
"starred-block"(commentGroup) {
|
114
|
+
const commentLines = getCommentLines(commentGroup);
|
115
|
+
|
116
|
+
if (commentLines.some(value => value.includes("*/"))) {
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
120
|
+
if (commentGroup.length > 1) {
|
121
|
+
context.report({
|
122
|
+
loc: {
|
123
|
+
start: commentGroup[0].loc.start,
|
124
|
+
end: commentGroup[commentGroup.length - 1].loc.end
|
125
|
+
},
|
126
|
+
message: EXPECTED_BLOCK_ERROR,
|
127
|
+
fix(fixer) {
|
128
|
+
const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
|
129
|
+
const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
|
130
|
+
|
131
|
+
return commentLines.some(value => value.startsWith("/"))
|
132
|
+
? null
|
133
|
+
: fixer.replaceTextRange(range, starredBlock);
|
134
|
+
}
|
135
|
+
});
|
136
|
+
} else {
|
137
|
+
const block = commentGroup[0];
|
138
|
+
const lines = block.value.split(astUtils.LINEBREAK_MATCHER);
|
139
|
+
const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`;
|
140
|
+
|
141
|
+
if (!/^\*?\s*$/.test(lines[0])) {
|
142
|
+
const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0];
|
143
|
+
|
144
|
+
context.report({
|
145
|
+
loc: {
|
146
|
+
start: block.loc.start,
|
147
|
+
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
|
148
|
+
},
|
149
|
+
message: START_NEWLINE_ERROR,
|
150
|
+
fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
|
151
|
+
});
|
152
|
+
}
|
153
|
+
|
154
|
+
if (!/^\s*$/.test(lines[lines.length - 1])) {
|
155
|
+
context.report({
|
156
|
+
loc: {
|
157
|
+
start: { line: block.loc.end.line, column: block.loc.end.column - 2 },
|
158
|
+
end: block.loc.end
|
159
|
+
},
|
160
|
+
message: END_NEWLINE_ERROR,
|
161
|
+
fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`)
|
162
|
+
});
|
163
|
+
}
|
164
|
+
|
165
|
+
for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) {
|
166
|
+
const lineText = sourceCode.lines[lineNumber - 1];
|
167
|
+
|
168
|
+
if (!lineText.startsWith(expectedLinePrefix)) {
|
169
|
+
context.report({
|
170
|
+
loc: {
|
171
|
+
start: { line: lineNumber, column: 0 },
|
172
|
+
end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length }
|
173
|
+
},
|
174
|
+
message: /^\s*\*/.test(lineText)
|
175
|
+
? ALIGNMENT_ERROR
|
176
|
+
: MISSING_STAR_ERROR,
|
177
|
+
fix(fixer) {
|
178
|
+
const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
|
179
|
+
const linePrefixLength = lineText.match(/^\s*\*? ?/)[0].length;
|
180
|
+
const commentStartIndex = lineStartIndex + linePrefixLength;
|
181
|
+
|
182
|
+
const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
|
183
|
+
? expectedLinePrefix
|
184
|
+
: `${expectedLinePrefix} `;
|
185
|
+
|
186
|
+
return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
|
187
|
+
}
|
188
|
+
});
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
},
|
193
|
+
"separate-lines"(commentGroup) {
|
194
|
+
if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
|
195
|
+
const commentLines = getCommentLines(commentGroup);
|
196
|
+
const block = commentGroup[0];
|
197
|
+
const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
|
198
|
+
|
199
|
+
if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
|
200
|
+
return;
|
201
|
+
}
|
202
|
+
|
203
|
+
context.report({
|
204
|
+
loc: {
|
205
|
+
start: block.loc.start,
|
206
|
+
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
|
207
|
+
},
|
208
|
+
message: EXPECTED_LINES_ERROR,
|
209
|
+
fix(fixer) {
|
210
|
+
return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line)));
|
211
|
+
}
|
212
|
+
});
|
213
|
+
}
|
214
|
+
},
|
215
|
+
"bare-block"(commentGroup) {
|
216
|
+
if (!isJSDoc(commentGroup)) {
|
217
|
+
const commentLines = getCommentLines(commentGroup);
|
218
|
+
|
219
|
+
// disallows consecutive line comments in favor of using a block comment.
|
220
|
+
if (commentGroup[0].type === "Line" && commentLines.length > 1 &&
|
221
|
+
!commentLines.some(value => value.includes("*/"))) {
|
222
|
+
context.report({
|
223
|
+
loc: {
|
224
|
+
start: commentGroup[0].loc.start,
|
225
|
+
end: commentGroup[commentGroup.length - 1].loc.end
|
226
|
+
},
|
227
|
+
message: EXPECTED_BLOCK_ERROR,
|
228
|
+
fix(fixer) {
|
229
|
+
const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
|
230
|
+
const block = convertToBlock(commentGroup[0], commentLines.filter(line => line));
|
231
|
+
|
232
|
+
return fixer.replaceTextRange(range, block);
|
233
|
+
}
|
234
|
+
});
|
235
|
+
}
|
236
|
+
|
237
|
+
// prohibits block comments from having a * at the beginning of each line.
|
238
|
+
if (commentGroup[0].type === "Block") {
|
239
|
+
const block = commentGroup[0];
|
240
|
+
const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim());
|
241
|
+
|
242
|
+
if (lines.length > 0 && lines.every(line => /^\s*\*/.test(line))) {
|
243
|
+
context.report({
|
244
|
+
loc: {
|
245
|
+
start: block.loc.start,
|
246
|
+
end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
|
247
|
+
},
|
248
|
+
message: EXPECTED_BLOCK_ERROR,
|
249
|
+
fix(fixer) {
|
250
|
+
return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line)));
|
251
|
+
}
|
252
|
+
});
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
};
|
258
|
+
|
259
|
+
//----------------------------------------------------------------------
|
260
|
+
// Public
|
261
|
+
//----------------------------------------------------------------------
|
262
|
+
|
263
|
+
return {
|
264
|
+
Program() {
|
265
|
+
return sourceCode.getAllComments()
|
266
|
+
.filter(comment => comment.type !== "Shebang")
|
267
|
+
.filter(comment => !astUtils.COMMENTS_IGNORE_PATTERN.test(comment.value))
|
268
|
+
.filter(comment => {
|
269
|
+
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
270
|
+
|
271
|
+
return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
|
272
|
+
})
|
273
|
+
.reduce((commentGroups, comment, index, commentList) => {
|
274
|
+
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
275
|
+
|
276
|
+
if (
|
277
|
+
comment.type === "Line" &&
|
278
|
+
index && commentList[index - 1].type === "Line" &&
|
279
|
+
tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
|
280
|
+
tokenBefore === commentList[index - 1]
|
281
|
+
) {
|
282
|
+
commentGroups[commentGroups.length - 1].push(comment);
|
283
|
+
} else {
|
284
|
+
commentGroups.push([comment]);
|
285
|
+
}
|
286
|
+
|
287
|
+
return commentGroups;
|
288
|
+
}, [])
|
289
|
+
.filter(commentGroup => !(commentGroup.length === 1 && commentGroup[0].loc.start.line === commentGroup[0].loc.end.line))
|
290
|
+
.forEach(commentGroupCheckers[option]);
|
291
|
+
}
|
292
|
+
};
|
293
|
+
}
|
294
|
+
};
|
package/lib/rules/new-cap.js
CHANGED
@@ -59,9 +59,11 @@ module.exports = {
|
|
59
59
|
|
60
60
|
if (parentType === "IfStatement") {
|
61
61
|
return isPrecededByTokens(node, ["else", ")"]);
|
62
|
-
}
|
62
|
+
}
|
63
|
+
if (parentType === "DoWhileStatement") {
|
63
64
|
return isPrecededByTokens(node, ["do"]);
|
64
|
-
}
|
65
|
+
}
|
66
|
+
if (parentType === "SwitchCase") {
|
65
67
|
return isPrecededByTokens(node, [":"]);
|
66
68
|
}
|
67
69
|
return isPrecededByTokens(node, [")"]);
|