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
@@ -0,0 +1,42 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Define the cursor which ignores the first few tokens.
|
3
|
+
* @author Toru Nagashima
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const DecorativeCursor = require("./decorative-cursor");
|
12
|
+
|
13
|
+
//------------------------------------------------------------------------------
|
14
|
+
// Exports
|
15
|
+
//------------------------------------------------------------------------------
|
16
|
+
|
17
|
+
/**
|
18
|
+
* The decorative cursor which ignores the first few tokens.
|
19
|
+
*/
|
20
|
+
module.exports = class SkipCursor extends DecorativeCursor {
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Initializes this cursor.
|
24
|
+
* @param {Cursor} cursor - The cursor to be decorated.
|
25
|
+
* @param {number} count - The count of tokens this cursor skips.
|
26
|
+
*/
|
27
|
+
constructor(cursor, count) {
|
28
|
+
super(cursor);
|
29
|
+
this.count = count;
|
30
|
+
}
|
31
|
+
|
32
|
+
/** @inheritdoc */
|
33
|
+
moveNext() {
|
34
|
+
while (this.count > 0) {
|
35
|
+
this.count -= 1;
|
36
|
+
if (!super.moveNext()) {
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
return super.moveNext();
|
41
|
+
}
|
42
|
+
};
|
@@ -0,0 +1,100 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Define utilify functions for token store.
|
3
|
+
* @author Toru Nagashima
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const lodash = require("lodash");
|
12
|
+
|
13
|
+
//------------------------------------------------------------------------------
|
14
|
+
// Helpers
|
15
|
+
//------------------------------------------------------------------------------
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Gets `token.range[0]` from the given token.
|
19
|
+
*
|
20
|
+
* @param {Node|Token|Comment} token - The token to get.
|
21
|
+
* @returns {number} The start location.
|
22
|
+
* @private
|
23
|
+
*/
|
24
|
+
function getStartLocation(token) {
|
25
|
+
return token.range[0];
|
26
|
+
}
|
27
|
+
|
28
|
+
//------------------------------------------------------------------------------
|
29
|
+
// Exports
|
30
|
+
//------------------------------------------------------------------------------
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Binary-searches the index of the first token which is after the given location.
|
34
|
+
* If it was not found, this returns `tokens.length`.
|
35
|
+
*
|
36
|
+
* @param {(Token|Comment)[]} tokens - It searches the token in this list.
|
37
|
+
* @param {number} location - The location to search.
|
38
|
+
* @returns {number} The found index or `tokens.length`.
|
39
|
+
*/
|
40
|
+
exports.search = function search(tokens, location) {
|
41
|
+
return lodash.sortedIndexBy(
|
42
|
+
tokens,
|
43
|
+
{ range: [location] },
|
44
|
+
getStartLocation
|
45
|
+
);
|
46
|
+
};
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Gets the index of the `startLoc` in `tokens`.
|
50
|
+
* `startLoc` can be the value of `node.range[1]`, so this checks about `startLoc - 1` as well.
|
51
|
+
*
|
52
|
+
* @param {(Token|Comment)[]} tokens - The tokens to find an index.
|
53
|
+
* @param {Object} indexMap - The map from locations to indices.
|
54
|
+
* @param {number} startLoc - The location to get an index.
|
55
|
+
* @returns {number} The index.
|
56
|
+
*/
|
57
|
+
exports.getFirstIndex = function getFirstIndex(tokens, indexMap, startLoc) {
|
58
|
+
if (startLoc in indexMap) {
|
59
|
+
return indexMap[startLoc];
|
60
|
+
}
|
61
|
+
if ((startLoc - 1) in indexMap) {
|
62
|
+
const index = indexMap[startLoc - 1];
|
63
|
+
const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
|
64
|
+
|
65
|
+
// For the map of "comment's location -> token's index", it points the next token of a comment.
|
66
|
+
// In that case, +1 is unnecessary.
|
67
|
+
if (token && token.range[0] >= startLoc) {
|
68
|
+
return index;
|
69
|
+
}
|
70
|
+
return index + 1;
|
71
|
+
}
|
72
|
+
return 0;
|
73
|
+
};
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Gets the index of the `endLoc` in `tokens`.
|
77
|
+
* The information of end locations are recorded at `endLoc - 1` in `indexMap`, so this checks about `endLoc - 1` as well.
|
78
|
+
*
|
79
|
+
* @param {(Token|Comment)[]} tokens - The tokens to find an index.
|
80
|
+
* @param {Object} indexMap - The map from locations to indices.
|
81
|
+
* @param {number} endLoc - The location to get an index.
|
82
|
+
* @returns {number} The index.
|
83
|
+
*/
|
84
|
+
exports.getLastIndex = function getLastIndex(tokens, indexMap, endLoc) {
|
85
|
+
if (endLoc in indexMap) {
|
86
|
+
return indexMap[endLoc] - 1;
|
87
|
+
}
|
88
|
+
if ((endLoc - 1) in indexMap) {
|
89
|
+
const index = indexMap[endLoc - 1];
|
90
|
+
const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
|
91
|
+
|
92
|
+
// For the map of "comment's location -> token's index", it points the next token of a comment.
|
93
|
+
// In that case, -1 is necessary.
|
94
|
+
if (token && token.range[1] > endLoc) {
|
95
|
+
return index - 1;
|
96
|
+
}
|
97
|
+
return index;
|
98
|
+
}
|
99
|
+
return tokens.length - 1;
|
100
|
+
};
|
@@ -0,0 +1,121 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Helper class to aid in constructing fix commands.
|
3
|
+
* @author Alan Pierce
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const astUtils = require("../ast-utils");
|
12
|
+
|
13
|
+
//------------------------------------------------------------------------------
|
14
|
+
// Public Interface
|
15
|
+
//------------------------------------------------------------------------------
|
16
|
+
|
17
|
+
/**
|
18
|
+
* A helper class to combine fix options into a fix command. Currently, it
|
19
|
+
* exposes some "retain" methods that extend the range of the text being
|
20
|
+
* replaced so that other fixes won't touch that region in the same pass.
|
21
|
+
*/
|
22
|
+
class FixTracker {
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Create a new FixTracker.
|
26
|
+
*
|
27
|
+
* @param {ruleFixer} fixer A ruleFixer instance.
|
28
|
+
* @param {SourceCode} sourceCode A SourceCode object for the current code.
|
29
|
+
*/
|
30
|
+
constructor(fixer, sourceCode) {
|
31
|
+
this.fixer = fixer;
|
32
|
+
this.sourceCode = sourceCode;
|
33
|
+
this.retainedRange = null;
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Mark the given range as "retained", meaning that other fixes may not
|
38
|
+
* may not modify this region in the same pass.
|
39
|
+
*
|
40
|
+
* @param {int[]} range The range to retain.
|
41
|
+
* @returns {FixTracker} The same RuleFixer, for chained calls.
|
42
|
+
*/
|
43
|
+
retainRange(range) {
|
44
|
+
this.retainedRange = range;
|
45
|
+
return this;
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Given a node, find the function containing it (or the entire program) and
|
50
|
+
* mark it as retained, meaning that other fixes may not modify it in this
|
51
|
+
* pass. This is useful for avoiding conflicts in fixes that modify control
|
52
|
+
* flow.
|
53
|
+
*
|
54
|
+
* @param {ASTNode} node The node to use as a starting point.
|
55
|
+
* @returns {FixTracker} The same RuleFixer, for chained calls.
|
56
|
+
*/
|
57
|
+
retainEnclosingFunction(node) {
|
58
|
+
const functionNode = astUtils.getUpperFunction(node);
|
59
|
+
|
60
|
+
return this.retainRange(
|
61
|
+
functionNode ? functionNode.range : this.sourceCode.ast.range);
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Given a node or token, find the token before and afterward, and mark that
|
66
|
+
* range as retained, meaning that other fixes may not modify it in this
|
67
|
+
* pass. This is useful for avoiding conflicts in fixes that make a small
|
68
|
+
* change to the code where the AST should not be changed.
|
69
|
+
*
|
70
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to use as a starting
|
71
|
+
* point. The token to the left and right are use in the range.
|
72
|
+
* @returns {FixTracker} The same RuleFixer, for chained calls.
|
73
|
+
*/
|
74
|
+
retainSurroundingTokens(nodeOrToken) {
|
75
|
+
const tokenBefore = this.sourceCode.getTokenBefore(nodeOrToken) || nodeOrToken;
|
76
|
+
const tokenAfter = this.sourceCode.getTokenAfter(nodeOrToken) || nodeOrToken;
|
77
|
+
|
78
|
+
return this.retainRange([tokenBefore.range[0], tokenAfter.range[1]]);
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Create a fix command that replaces the given range with the given text,
|
83
|
+
* accounting for any retained ranges.
|
84
|
+
*
|
85
|
+
* @param {int[]} range The range to remove in the fix.
|
86
|
+
* @param {string} text The text to insert in place of the range.
|
87
|
+
* @returns {Object} The fix command.
|
88
|
+
*/
|
89
|
+
replaceTextRange(range, text) {
|
90
|
+
let actualRange;
|
91
|
+
|
92
|
+
if (this.retainedRange) {
|
93
|
+
actualRange = [
|
94
|
+
Math.min(this.retainedRange[0], range[0]),
|
95
|
+
Math.max(this.retainedRange[1], range[1])
|
96
|
+
];
|
97
|
+
} else {
|
98
|
+
actualRange = range;
|
99
|
+
}
|
100
|
+
|
101
|
+
return this.fixer.replaceTextRange(
|
102
|
+
actualRange,
|
103
|
+
this.sourceCode.text.slice(actualRange[0], range[0]) +
|
104
|
+
text +
|
105
|
+
this.sourceCode.text.slice(range[1], actualRange[1])
|
106
|
+
);
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Create a fix command that removes the given node or token, accounting for
|
111
|
+
* any retained ranges.
|
112
|
+
*
|
113
|
+
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
|
114
|
+
* @returns {Object} The fix command.
|
115
|
+
*/
|
116
|
+
remove(nodeOrToken) {
|
117
|
+
return this.replaceTextRange(nodeOrToken.range, "");
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
module.exports = FixTracker;
|
@@ -16,6 +16,17 @@ const debug = require("debug")("eslint:text-fixer");
|
|
16
16
|
|
17
17
|
const BOM = "\uFEFF";
|
18
18
|
|
19
|
+
/**
|
20
|
+
* Compares items in a messages array by range.
|
21
|
+
* @param {Message} a The first message.
|
22
|
+
* @param {Message} b The second message.
|
23
|
+
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
24
|
+
* @private
|
25
|
+
*/
|
26
|
+
function compareMessagesByFixRange(a, b) {
|
27
|
+
return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
|
28
|
+
}
|
29
|
+
|
19
30
|
/**
|
20
31
|
* Compares items in a messages array by line and column.
|
21
32
|
* @param {Message} a The first message.
|
@@ -24,13 +35,7 @@ const BOM = "\uFEFF";
|
|
24
35
|
* @private
|
25
36
|
*/
|
26
37
|
function compareMessagesByLocation(a, b) {
|
27
|
-
|
28
|
-
|
29
|
-
if (lineDiff === 0) {
|
30
|
-
return a.column - b.column;
|
31
|
-
}
|
32
|
-
return lineDiff;
|
33
|
-
|
38
|
+
return a.line - b.line || a.column - b.column;
|
34
39
|
}
|
35
40
|
|
36
41
|
//------------------------------------------------------------------------------
|
@@ -68,9 +73,10 @@ SourceCodeFixer.applyFixes = function(sourceCode, messages) {
|
|
68
73
|
// clone the array
|
69
74
|
const remainingMessages = [],
|
70
75
|
fixes = [],
|
76
|
+
bom = (sourceCode.hasBOM ? BOM : ""),
|
71
77
|
text = sourceCode.text;
|
72
|
-
let
|
73
|
-
|
78
|
+
let lastPos = Number.NEGATIVE_INFINITY,
|
79
|
+
output = bom;
|
74
80
|
|
75
81
|
messages.forEach(problem => {
|
76
82
|
if (problem.hasOwnProperty("fix")) {
|
@@ -83,51 +89,41 @@ SourceCodeFixer.applyFixes = function(sourceCode, messages) {
|
|
83
89
|
if (fixes.length) {
|
84
90
|
debug("Found fixes to apply");
|
85
91
|
|
86
|
-
|
87
|
-
fixes.sort((a, b) => b.fix.range[1] - a.fix.range[1] || b.fix.range[0] - a.fix.range[0]);
|
88
|
-
|
89
|
-
// split into array of characters for easier manipulation
|
90
|
-
const chars = text.split("");
|
91
|
-
|
92
|
-
fixes.forEach(problem => {
|
92
|
+
for (const problem of fixes.sort(compareMessagesByFixRange)) {
|
93
93
|
const fix = problem.fix;
|
94
|
-
|
94
|
+
const start = fix.range[0];
|
95
95
|
const end = fix.range[1];
|
96
|
-
let insertionText = fix.text;
|
97
|
-
|
98
|
-
if (end < lastFixPos) {
|
99
|
-
if (start < 0) {
|
100
|
-
|
101
|
-
// Remove BOM.
|
102
|
-
prefix = "";
|
103
|
-
start = 0;
|
104
|
-
}
|
105
|
-
|
106
|
-
if (start === 0 && insertionText[0] === BOM) {
|
107
|
-
|
108
|
-
// Set BOM.
|
109
|
-
prefix = BOM;
|
110
|
-
insertionText = insertionText.slice(1);
|
111
|
-
}
|
112
96
|
|
113
|
-
|
114
|
-
|
115
|
-
} else {
|
97
|
+
// Remain it as a problem if it's overlapped or it's a negative range
|
98
|
+
if (lastPos >= start || start > end) {
|
116
99
|
remainingMessages.push(problem);
|
100
|
+
continue;
|
117
101
|
}
|
118
|
-
|
102
|
+
|
103
|
+
// Remove BOM.
|
104
|
+
if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
|
105
|
+
output = "";
|
106
|
+
}
|
107
|
+
|
108
|
+
// Make output to this fix.
|
109
|
+
output += text.slice(Math.max(0, lastPos), Math.max(0, start));
|
110
|
+
output += fix.text;
|
111
|
+
lastPos = end;
|
112
|
+
}
|
113
|
+
output += text.slice(Math.max(0, lastPos));
|
119
114
|
|
120
115
|
return {
|
121
116
|
fixed: true,
|
122
117
|
messages: remainingMessages.sort(compareMessagesByLocation),
|
123
|
-
output
|
118
|
+
output
|
124
119
|
};
|
125
120
|
}
|
121
|
+
|
126
122
|
debug("No fixes to apply");
|
127
123
|
return {
|
128
124
|
fixed: false,
|
129
125
|
messages,
|
130
|
-
output:
|
126
|
+
output: bom + text
|
131
127
|
};
|
132
128
|
|
133
129
|
};
|
package/lib/util/source-code.js
CHANGED
@@ -8,8 +8,10 @@
|
|
8
8
|
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const
|
12
|
-
Traverser = require("./traverser")
|
11
|
+
const TokenStore = require("../token-store"),
|
12
|
+
Traverser = require("./traverser"),
|
13
|
+
astUtils = require("../ast-utils"),
|
14
|
+
lodash = require("lodash");
|
13
15
|
|
14
16
|
//------------------------------------------------------------------------------
|
15
17
|
// Private
|
@@ -77,6 +79,28 @@ function looksLikeExport(astNode) {
|
|
77
79
|
astNode.type === "ExportAllDeclaration" || astNode.type === "ExportSpecifier";
|
78
80
|
}
|
79
81
|
|
82
|
+
/**
|
83
|
+
* Merges two sorted lists into a larger sorted list in O(n) time
|
84
|
+
* @param {Token[]} tokens The list of tokens
|
85
|
+
* @param {Token[]} comments The list of comments
|
86
|
+
* @returns {Token[]} A sorted list of tokens and comments
|
87
|
+
*/
|
88
|
+
function sortedMerge(tokens, comments) {
|
89
|
+
const result = [];
|
90
|
+
let tokenIndex = 0;
|
91
|
+
let commentIndex = 0;
|
92
|
+
|
93
|
+
while (tokenIndex < tokens.length || commentIndex < comments.length) {
|
94
|
+
if (commentIndex >= comments.length || tokenIndex < tokens.length && tokens[tokenIndex].range[0] < comments[commentIndex].range[0]) {
|
95
|
+
result.push(tokens[tokenIndex++]);
|
96
|
+
} else {
|
97
|
+
result.push(comments[commentIndex++]);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
return result;
|
102
|
+
}
|
103
|
+
|
80
104
|
|
81
105
|
//------------------------------------------------------------------------------
|
82
106
|
// Public Interface
|
@@ -115,23 +139,35 @@ function SourceCode(text, ast) {
|
|
115
139
|
* This is done to avoid each rule needing to do so separately.
|
116
140
|
* @type string[]
|
117
141
|
*/
|
118
|
-
this.lines =
|
142
|
+
this.lines = [];
|
143
|
+
this.lineStartIndices = [0];
|
144
|
+
|
145
|
+
const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
|
146
|
+
let match;
|
147
|
+
|
148
|
+
/*
|
149
|
+
* Previously, this was implemented using a regex that
|
150
|
+
* matched a sequence of non-linebreak characters followed by a
|
151
|
+
* linebreak, then adding the lengths of the matches. However,
|
152
|
+
* this caused a catastrophic backtracking issue when the end
|
153
|
+
* of a file contained a large number of non-newline characters.
|
154
|
+
* To avoid this, the current implementation just matches newlines
|
155
|
+
* and uses match.index to get the correct line start indices.
|
156
|
+
*/
|
157
|
+
while ((match = lineEndingPattern.exec(this.text))) {
|
158
|
+
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
|
159
|
+
this.lineStartIndices.push(match.index + match[0].length);
|
160
|
+
}
|
161
|
+
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
|
119
162
|
|
120
|
-
this.tokensAndComments = ast.tokens
|
121
|
-
.concat(ast.comments)
|
122
|
-
.sort((left, right) => left.range[0] - right.range[0]);
|
163
|
+
this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
|
123
164
|
|
124
165
|
// create token store methods
|
125
|
-
const tokenStore =
|
126
|
-
|
127
|
-
Object.keys(tokenStore).forEach(methodName => {
|
128
|
-
this[methodName] = tokenStore[methodName];
|
129
|
-
});
|
130
|
-
|
131
|
-
const tokensAndCommentsStore = createTokenStore(this.tokensAndComments);
|
166
|
+
const tokenStore = new TokenStore(ast.tokens, ast.comments);
|
132
167
|
|
133
|
-
|
134
|
-
|
168
|
+
for (const methodName of TokenStore.PUBLIC_METHODS) {
|
169
|
+
this[methodName] = tokenStore[methodName].bind(tokenStore);
|
170
|
+
}
|
135
171
|
|
136
172
|
// don't allow modification of this object
|
137
173
|
Object.freeze(this);
|
@@ -145,7 +181,7 @@ function SourceCode(text, ast) {
|
|
145
181
|
* @public
|
146
182
|
*/
|
147
183
|
SourceCode.splitLines = function(text) {
|
148
|
-
return text.split(
|
184
|
+
return text.split(astUtils.createGlobalLinebreakMatcher());
|
149
185
|
};
|
150
186
|
|
151
187
|
SourceCode.prototype = {
|
@@ -296,6 +332,83 @@ SourceCode.prototype = {
|
|
296
332
|
const text = this.text.slice(first.range[1], second.range[0]);
|
297
333
|
|
298
334
|
return /\s/.test(text.replace(/\/\*.*?\*\//g, ""));
|
335
|
+
},
|
336
|
+
|
337
|
+
/**
|
338
|
+
* Converts a source text index into a (line, column) pair.
|
339
|
+
* @param {number} index The index of a character in a file
|
340
|
+
* @returns {Object} A {line, column} location object with a 0-indexed column
|
341
|
+
*/
|
342
|
+
getLocFromIndex(index) {
|
343
|
+
if (typeof index !== "number") {
|
344
|
+
throw new TypeError("Expected `index` to be a number.");
|
345
|
+
}
|
346
|
+
|
347
|
+
if (index < 0 || index > this.text.length) {
|
348
|
+
throw new RangeError(`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`);
|
349
|
+
}
|
350
|
+
|
351
|
+
/*
|
352
|
+
* For an argument of this.text.length, return the location one "spot" past the last character
|
353
|
+
* of the file. If the last character is a linebreak, the location will be column 0 of the next
|
354
|
+
* line; otherwise, the location will be in the next column on the same line.
|
355
|
+
*
|
356
|
+
* See getIndexFromLoc for the motivation for this special case.
|
357
|
+
*/
|
358
|
+
if (index === this.text.length) {
|
359
|
+
return { line: this.lines.length, column: this.lines[this.lines.length - 1].length };
|
360
|
+
}
|
361
|
+
|
362
|
+
/*
|
363
|
+
* To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
|
364
|
+
* be inserted into lineIndices to keep the list sorted.
|
365
|
+
*/
|
366
|
+
const lineNumber = lodash.sortedLastIndex(this.lineStartIndices, index);
|
367
|
+
|
368
|
+
return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] };
|
369
|
+
|
370
|
+
},
|
371
|
+
|
372
|
+
/**
|
373
|
+
* Converts a (line, column) pair into a range index.
|
374
|
+
* @param {Object} loc A line/column location
|
375
|
+
* @param {number} loc.line The line number of the location (1-indexed)
|
376
|
+
* @param {number} loc.column The column number of the location (0-indexed)
|
377
|
+
* @returns {number} The range index of the location in the file.
|
378
|
+
*/
|
379
|
+
getIndexFromLoc(loc) {
|
380
|
+
if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
|
381
|
+
throw new TypeError("Expected `loc` to be an object with numeric `line` and `column` properties.");
|
382
|
+
}
|
383
|
+
|
384
|
+
if (loc.line <= 0) {
|
385
|
+
throw new RangeError(`Line number out of range (line ${loc.line} requested). Line numbers should be 1-based.`);
|
386
|
+
}
|
387
|
+
|
388
|
+
if (loc.line > this.lineStartIndices.length) {
|
389
|
+
throw new RangeError(`Line number out of range (line ${loc.line} requested, but only ${this.lineStartIndices.length} lines present).`);
|
390
|
+
}
|
391
|
+
|
392
|
+
const lineStartIndex = this.lineStartIndices[loc.line - 1];
|
393
|
+
const lineEndIndex = loc.line === this.lineStartIndices.length ? this.text.length : this.lineStartIndices[loc.line];
|
394
|
+
const positionIndex = lineStartIndex + loc.column;
|
395
|
+
|
396
|
+
/*
|
397
|
+
* By design, getIndexFromLoc({ line: lineNum, column: 0 }) should return the start index of
|
398
|
+
* the given line, provided that the line number is valid element of this.lines. Since the
|
399
|
+
* last element of this.lines is an empty string for files with trailing newlines, add a
|
400
|
+
* special case where getting the index for the first location after the end of the file
|
401
|
+
* will return the length of the file, rather than throwing an error. This allows rules to
|
402
|
+
* use getIndexFromLoc consistently without worrying about edge cases at the end of a file.
|
403
|
+
*/
|
404
|
+
if (
|
405
|
+
loc.line === this.lineStartIndices.length && positionIndex > lineEndIndex ||
|
406
|
+
loc.line < this.lineStartIndices.length && positionIndex >= lineEndIndex
|
407
|
+
) {
|
408
|
+
throw new RangeError(`Column number out of range (column ${loc.column} requested, but the length of line ${loc.line} is ${lineEndIndex - lineStartIndex}).`);
|
409
|
+
}
|
410
|
+
|
411
|
+
return positionIndex;
|
299
412
|
}
|
300
413
|
};
|
301
414
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.17.1",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -78,14 +78,14 @@
|
|
78
78
|
"browserify": "^12.0.1",
|
79
79
|
"chai": "^3.5.0",
|
80
80
|
"cheerio": "^0.19.0",
|
81
|
-
"coveralls": "2.11.
|
81
|
+
"coveralls": "^2.11.16",
|
82
82
|
"dateformat": "^1.0.8",
|
83
83
|
"ejs": "^2.3.3",
|
84
|
-
"eslint-plugin-
|
84
|
+
"eslint-plugin-eslint-plugin": "^0.7.1",
|
85
|
+
"eslint-plugin-node": "^4.1.0",
|
85
86
|
"eslint-release": "^0.10.0",
|
86
87
|
"esprima": "^2.4.1",
|
87
88
|
"esprima-fb": "^15001.1001.0-dev-harmony-fb",
|
88
|
-
"gh-got": "^2.2.0",
|
89
89
|
"istanbul": "^0.4.0",
|
90
90
|
"jsdoc": "^3.3.0-beta1",
|
91
91
|
"karma": "^0.13.22",
|
@@ -94,11 +94,10 @@
|
|
94
94
|
"karma-mocha-reporter": "^2.0.3",
|
95
95
|
"karma-phantomjs-launcher": "^1.0.0",
|
96
96
|
"leche": "^2.1.1",
|
97
|
-
"linefix": "^0.1.1",
|
98
97
|
"load-perf": "^0.2.0",
|
99
98
|
"markdownlint": "^0.3.1",
|
100
99
|
"mocha": "^2.4.5",
|
101
|
-
"mock-fs": "
|
100
|
+
"mock-fs": "not-an-aardvark/mock-fs#06868bbd7724707f9324b237bdde28f05f7a01d5",
|
102
101
|
"npm-license": "^0.3.2",
|
103
102
|
"phantomjs-prebuilt": "^2.1.7",
|
104
103
|
"proxyquire": "^1.7.10",
|