eslint 4.5.0 → 4.7.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 +98 -0
- package/bin/eslint.js +2 -1
- package/conf/eslint-recommended.js +1 -0
- package/lib/ast-utils.js +20 -17
- package/lib/cli-engine.js +51 -124
- package/lib/code-path-analysis/code-path-analyzer.js +8 -4
- package/lib/code-path-analysis/code-path-segment.js +2 -1
- package/lib/code-path-analysis/code-path-state.js +21 -14
- package/lib/code-path-analysis/code-path.js +3 -2
- package/lib/code-path-analysis/fork-context.js +2 -1
- package/lib/config/autoconfig.js +2 -4
- package/lib/config/config-initializer.js +9 -5
- package/lib/config/config-ops.js +15 -15
- package/lib/config.js +8 -12
- package/lib/formatters/codeframe.js +1 -1
- package/lib/formatters/stylish.js +1 -1
- package/lib/ignored-paths.js +0 -2
- package/lib/linter.js +468 -638
- package/lib/report-translator.js +274 -0
- package/lib/rules/function-paren-newline.js +221 -0
- package/lib/rules/generator-star-spacing.js +70 -19
- package/lib/rules/indent-legacy.js +3 -2
- package/lib/rules/indent.js +15 -6
- package/lib/rules/key-spacing.js +2 -1
- package/lib/rules/newline-per-chained-call.js +20 -3
- package/lib/rules/no-extra-parens.js +75 -33
- package/lib/rules/no-invalid-this.js +2 -1
- package/lib/rules/no-tabs.js +1 -1
- package/lib/rules/no-undef-init.js +4 -0
- package/lib/rules/no-unmodified-loop-condition.js +1 -1
- package/lib/rules/no-unused-vars.js +47 -4
- package/lib/rules/padded-blocks.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +1 -2
- package/lib/rules/quote-props.js +4 -2
- package/lib/rules/quotes.js +1 -2
- package/lib/rules/space-before-blocks.js +1 -1
- package/lib/rules/valid-jsdoc.js +2 -2
- package/lib/rules.js +48 -3
- package/lib/testers/rule-tester.js +27 -51
- package/lib/timing.js +2 -2
- package/lib/util/apply-disable-directives.js +131 -0
- package/lib/util/fix-tracker.js +1 -2
- package/lib/util/npm-util.js +21 -4
- package/lib/util/source-code-fixer.js +5 -14
- package/lib/util/source-code.js +3 -5
- package/package.json +8 -8
- package/lib/rule-context.js +0 -241
- package/lib/testers/event-generator-tester.js +0 -62
- package/lib/testers/test-parser.js +0 -48
@@ -159,7 +159,8 @@ class RuleTester {
|
|
159
159
|
|
160
160
|
// we have to clone because merge uses the first argument for recipient
|
161
161
|
lodash.cloneDeep(defaultConfig),
|
162
|
-
testerConfig
|
162
|
+
testerConfig,
|
163
|
+
{ rules: { "rule-tester/validate-ast": "error" } }
|
163
164
|
);
|
164
165
|
|
165
166
|
/**
|
@@ -306,7 +307,17 @@ class RuleTester {
|
|
306
307
|
config.rules[ruleName] = 1;
|
307
308
|
}
|
308
309
|
|
309
|
-
linter.defineRule(ruleName, rule
|
310
|
+
linter.defineRule(ruleName, Object.assign({}, rule, {
|
311
|
+
|
312
|
+
// Create a wrapper rule that freezes the `context` properties.
|
313
|
+
create(context) {
|
314
|
+
freezeDeeply(context.options);
|
315
|
+
freezeDeeply(context.settings);
|
316
|
+
freezeDeeply(context.parserOptions);
|
317
|
+
|
318
|
+
return (typeof rule === "function" ? rule : rule.create)(context);
|
319
|
+
}
|
320
|
+
}));
|
310
321
|
|
311
322
|
const schema = validator.getRuleOptionsSchema(ruleName, linter.rules);
|
312
323
|
|
@@ -331,55 +342,20 @@ class RuleTester {
|
|
331
342
|
* The goal is to check whether or not AST was modified when
|
332
343
|
* running the rule under test.
|
333
344
|
*/
|
334
|
-
linter.
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
});
|
343
|
-
|
344
|
-
// Freezes rule-context properties.
|
345
|
-
const originalGet = linter.rules.get;
|
346
|
-
|
347
|
-
try {
|
348
|
-
linter.rules.get = function(ruleId) {
|
349
|
-
const originalRule = originalGet.call(linter.rules, ruleId);
|
350
|
-
|
351
|
-
if (typeof originalRule === "function") {
|
352
|
-
return function(context) {
|
353
|
-
Object.freeze(context);
|
354
|
-
freezeDeeply(context.options);
|
355
|
-
freezeDeeply(context.settings);
|
356
|
-
freezeDeeply(context.parserOptions);
|
357
|
-
|
358
|
-
return originalRule(context);
|
359
|
-
};
|
360
|
-
}
|
361
|
-
return {
|
362
|
-
meta: originalRule.meta,
|
363
|
-
create(context) {
|
364
|
-
Object.freeze(context);
|
365
|
-
freezeDeeply(context.options);
|
366
|
-
freezeDeeply(context.settings);
|
367
|
-
freezeDeeply(context.parserOptions);
|
368
|
-
|
369
|
-
return originalRule.create(context);
|
370
|
-
}
|
371
|
-
};
|
372
|
-
|
373
|
-
};
|
345
|
+
linter.defineRule("rule-tester/validate-ast", () => ({
|
346
|
+
Program(node) {
|
347
|
+
beforeAST = cloneDeeplyExcludesParent(node);
|
348
|
+
},
|
349
|
+
"Program:exit"(node) {
|
350
|
+
afterAST = node;
|
351
|
+
}
|
352
|
+
}));
|
374
353
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
} finally {
|
381
|
-
linter.rules.get = originalGet;
|
382
|
-
}
|
354
|
+
return {
|
355
|
+
messages: linter.verify(code, config, filename, true),
|
356
|
+
beforeAST,
|
357
|
+
afterAST: cloneDeeplyExcludesParent(afterAST)
|
358
|
+
};
|
383
359
|
}
|
384
360
|
|
385
361
|
/**
|
@@ -519,7 +495,7 @@ class RuleTester {
|
|
519
495
|
"Expected no autofixes to be suggested"
|
520
496
|
);
|
521
497
|
} else {
|
522
|
-
const fixResult = SourceCodeFixer.applyFixes(
|
498
|
+
const fixResult = SourceCodeFixer.applyFixes(item.code, messages);
|
523
499
|
|
524
500
|
assert.equal(fixResult.output, item.output, "Output is incorrect.");
|
525
501
|
}
|
package/lib/timing.js
CHANGED
@@ -84,11 +84,11 @@ function display(data) {
|
|
84
84
|
}
|
85
85
|
});
|
86
86
|
|
87
|
-
const table = rows.map(row =>
|
87
|
+
const table = rows.map(row => (
|
88
88
|
row
|
89
89
|
.map((cell, index) => ALIGN[index](cell, widths[index]))
|
90
90
|
.join(" | ")
|
91
|
-
);
|
91
|
+
));
|
92
92
|
|
93
93
|
table.splice(1, 0, widths.map((w, index) => {
|
94
94
|
if (index !== 0 && index !== widths.length - 1) {
|
@@ -0,0 +1,131 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview A module that filters reported problems based on `eslint-disable` and `eslint-enable` comments
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
const lodash = require("lodash");
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Compares the locations of two objects in a source file
|
12
|
+
* @param {{line: number, column: number}} itemA The first object
|
13
|
+
* @param {{line: number, column: number}} itemB The second object
|
14
|
+
* @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
|
15
|
+
* itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
|
16
|
+
*/
|
17
|
+
function compareLocations(itemA, itemB) {
|
18
|
+
return itemA.line - itemB.line || itemA.column - itemB.column;
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* This is the same as the exported function, except that it doesn't handle disable-line and disable-next-line directives.
|
23
|
+
* @param {Object} options options (see the exported function)
|
24
|
+
* @returns {Problem[]} Filtered problems (see the exported function)
|
25
|
+
*/
|
26
|
+
function applyDirectives(options) {
|
27
|
+
const problems = [];
|
28
|
+
let nextDirectiveIndex = 0;
|
29
|
+
let globalDisableActive = false;
|
30
|
+
|
31
|
+
// disabledRules is only used when there is no active global /* eslint-disable */ comment.
|
32
|
+
const disabledRules = new Set();
|
33
|
+
|
34
|
+
// enabledRules is only used when there is an active global /* eslint-disable */ comment.
|
35
|
+
const enabledRules = new Set();
|
36
|
+
|
37
|
+
for (const problem of options.problems) {
|
38
|
+
while (
|
39
|
+
nextDirectiveIndex < options.directives.length &&
|
40
|
+
compareLocations(options.directives[nextDirectiveIndex], problem) <= 0
|
41
|
+
) {
|
42
|
+
const directive = options.directives[nextDirectiveIndex++];
|
43
|
+
|
44
|
+
switch (directive.type) {
|
45
|
+
case "disable":
|
46
|
+
if (directive.ruleId === null) {
|
47
|
+
globalDisableActive = true;
|
48
|
+
enabledRules.clear();
|
49
|
+
} else if (globalDisableActive) {
|
50
|
+
enabledRules.delete(directive.ruleId);
|
51
|
+
} else {
|
52
|
+
disabledRules.add(directive.ruleId);
|
53
|
+
}
|
54
|
+
break;
|
55
|
+
|
56
|
+
case "enable":
|
57
|
+
if (directive.ruleId === null) {
|
58
|
+
globalDisableActive = false;
|
59
|
+
disabledRules.clear();
|
60
|
+
} else if (globalDisableActive) {
|
61
|
+
enabledRules.add(directive.ruleId);
|
62
|
+
} else {
|
63
|
+
disabledRules.delete(directive.ruleId);
|
64
|
+
}
|
65
|
+
break;
|
66
|
+
|
67
|
+
// no default
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
if (
|
72
|
+
globalDisableActive && enabledRules.has(problem.ruleId) ||
|
73
|
+
!globalDisableActive && !disabledRules.has(problem.ruleId)
|
74
|
+
) {
|
75
|
+
problems.push(problem);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
return problems;
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
|
84
|
+
* of reported problems, determines which problems should be reported.
|
85
|
+
* @param {Object} options Information about directives and problems
|
86
|
+
* @param {{
|
87
|
+
* type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
88
|
+
* ruleId: (string|null),
|
89
|
+
* line: number,
|
90
|
+
* column: number
|
91
|
+
* }} options.directives Directive comments found in the file, with one-based columns.
|
92
|
+
* Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable
|
93
|
+
* comment for two different rules is represented as two directives).
|
94
|
+
* @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
|
95
|
+
* A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
|
96
|
+
* @returns {{ruleId: (string|null), line: number, column: number}[]}
|
97
|
+
* A list of reported problems that were not disabled by the directive comments.
|
98
|
+
*/
|
99
|
+
module.exports = options => {
|
100
|
+
const blockDirectives = options.directives
|
101
|
+
.filter(directive => directive.type === "disable" || directive.type === "enable")
|
102
|
+
.sort(compareLocations);
|
103
|
+
|
104
|
+
const lineDirectives = lodash.flatMap(options.directives, directive => {
|
105
|
+
switch (directive.type) {
|
106
|
+
case "disable":
|
107
|
+
case "enable":
|
108
|
+
return [];
|
109
|
+
|
110
|
+
case "disable-line":
|
111
|
+
return [
|
112
|
+
{ type: "disable", line: directive.line, column: 1, ruleId: directive.ruleId },
|
113
|
+
{ type: "enable", line: directive.line + 1, column: 0, ruleId: directive.ruleId }
|
114
|
+
];
|
115
|
+
|
116
|
+
case "disable-next-line":
|
117
|
+
return [
|
118
|
+
{ type: "disable", line: directive.line + 1, column: 1, ruleId: directive.ruleId },
|
119
|
+
{ type: "enable", line: directive.line + 2, column: 0, ruleId: directive.ruleId }
|
120
|
+
];
|
121
|
+
|
122
|
+
default:
|
123
|
+
throw new TypeError(`Unrecognized directive type '${directive.type}'`);
|
124
|
+
}
|
125
|
+
}).sort(compareLocations);
|
126
|
+
|
127
|
+
const problemsAfterBlockDirectives = applyDirectives({ problems: options.problems, directives: blockDirectives });
|
128
|
+
const problemsAfterLineDirectives = applyDirectives({ problems: problemsAfterBlockDirectives, directives: lineDirectives });
|
129
|
+
|
130
|
+
return problemsAfterLineDirectives.sort(compareLocations);
|
131
|
+
};
|
package/lib/util/fix-tracker.js
CHANGED
@@ -57,8 +57,7 @@ class FixTracker {
|
|
57
57
|
retainEnclosingFunction(node) {
|
58
58
|
const functionNode = astUtils.getUpperFunction(node);
|
59
59
|
|
60
|
-
return this.retainRange(
|
61
|
-
functionNode ? functionNode.range : this.sourceCode.ast.range);
|
60
|
+
return this.retainRange(functionNode ? functionNode.range : this.sourceCode.ast.range);
|
62
61
|
}
|
63
62
|
|
64
63
|
/**
|
package/lib/util/npm-util.js
CHANGED
@@ -53,22 +53,39 @@ function installSyncSaveDev(packages) {
|
|
53
53
|
if (!Array.isArray(packages)) {
|
54
54
|
packages = [packages];
|
55
55
|
}
|
56
|
-
spawn.sync("npm", ["i", "--save-dev"].concat(packages),
|
56
|
+
const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packages),
|
57
|
+
{ stdio: "inherit" });
|
58
|
+
const error = npmProcess.error;
|
59
|
+
|
60
|
+
if (error && error.code === "ENOENT") {
|
61
|
+
const pluralS = packages.length > 1 ? "s" : "";
|
62
|
+
|
63
|
+
log.error(`Could not execute npm. Please install the following package${pluralS} with your package manager of choice: ${packages.join(", ")}`);
|
64
|
+
}
|
57
65
|
}
|
58
66
|
|
59
67
|
/**
|
60
68
|
* Fetch `peerDependencies` of the given package by `npm show` command.
|
61
69
|
* @param {string} packageName The package name to fetch peerDependencies.
|
62
|
-
* @returns {Object} Gotten peerDependencies.
|
70
|
+
* @returns {Object} Gotten peerDependencies. Returns null if npm was not found.
|
63
71
|
*/
|
64
72
|
function fetchPeerDependencies(packageName) {
|
65
|
-
const
|
73
|
+
const npmProcess = spawn.sync(
|
66
74
|
"npm",
|
67
75
|
["show", "--json", packageName, "peerDependencies"],
|
68
76
|
{ encoding: "utf8" }
|
69
|
-
)
|
77
|
+
);
|
78
|
+
|
79
|
+
const error = npmProcess.error;
|
80
|
+
|
81
|
+
if (error && error.code === "ENOENT") {
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
const fetchedText = npmProcess.stdout.trim();
|
70
85
|
|
71
86
|
return JSON.parse(fetchedText || "{}");
|
87
|
+
|
88
|
+
|
72
89
|
}
|
73
90
|
|
74
91
|
/**
|
@@ -53,37 +53,28 @@ function SourceCodeFixer() {
|
|
53
53
|
/**
|
54
54
|
* Applies the fixes specified by the messages to the given text. Tries to be
|
55
55
|
* smart about the fixes and won't apply fixes over the same area in the text.
|
56
|
-
* @param {
|
56
|
+
* @param {string} sourceText The text to apply the changes to.
|
57
57
|
* @param {Message[]} messages The array of messages reported by ESLint.
|
58
58
|
* @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
|
59
59
|
* @returns {Object} An object containing the fixed text and any unfixed messages.
|
60
60
|
*/
|
61
|
-
SourceCodeFixer.applyFixes = function(
|
61
|
+
SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
|
62
62
|
debug("Applying fixes");
|
63
63
|
|
64
|
-
if (!sourceCode) {
|
65
|
-
debug("No source code to fix");
|
66
|
-
return {
|
67
|
-
fixed: false,
|
68
|
-
messages,
|
69
|
-
output: ""
|
70
|
-
};
|
71
|
-
}
|
72
|
-
|
73
64
|
if (shouldFix === false) {
|
74
65
|
debug("shouldFix parameter was false, not attempting fixes");
|
75
66
|
return {
|
76
67
|
fixed: false,
|
77
68
|
messages,
|
78
|
-
output:
|
69
|
+
output: sourceText
|
79
70
|
};
|
80
71
|
}
|
81
72
|
|
82
73
|
// clone the array
|
83
74
|
const remainingMessages = [],
|
84
75
|
fixes = [],
|
85
|
-
bom = (
|
86
|
-
text =
|
76
|
+
bom = sourceText.startsWith(BOM) ? BOM : "",
|
77
|
+
text = bom ? sourceText.slice(1) : sourceText;
|
87
78
|
let lastPos = Number.NEGATIVE_INFINITY,
|
88
79
|
output = bom;
|
89
80
|
|
package/lib/util/source-code.js
CHANGED
@@ -349,15 +349,13 @@ class SourceCode extends TokenStore {
|
|
349
349
|
* @returns {ASTNode} The node if found or null if not found.
|
350
350
|
*/
|
351
351
|
getNodeByRangeIndex(index) {
|
352
|
-
let result = null
|
353
|
-
resultParent = null;
|
352
|
+
let result = null;
|
354
353
|
const traverser = new Traverser();
|
355
354
|
|
356
355
|
traverser.traverse(this.ast, {
|
357
|
-
enter(node
|
356
|
+
enter(node) {
|
358
357
|
if (node.range[0] <= index && index < node.range[1]) {
|
359
358
|
result = node;
|
360
|
-
resultParent = parent;
|
361
359
|
} else {
|
362
360
|
this.skip();
|
363
361
|
}
|
@@ -369,7 +367,7 @@ class SourceCode extends TokenStore {
|
|
369
367
|
}
|
370
368
|
});
|
371
369
|
|
372
|
-
return result
|
370
|
+
return result;
|
373
371
|
}
|
374
372
|
|
375
373
|
/**
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.7.1",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -40,10 +40,10 @@
|
|
40
40
|
"chalk": "^2.1.0",
|
41
41
|
"concat-stream": "^1.6.0",
|
42
42
|
"cross-spawn": "^5.1.0",
|
43
|
-
"debug": "^
|
43
|
+
"debug": "^3.0.1",
|
44
44
|
"doctrine": "^2.0.0",
|
45
45
|
"eslint-scope": "^3.7.1",
|
46
|
-
"espree": "^3.5.
|
46
|
+
"espree": "^3.5.1",
|
47
47
|
"esquery": "^1.0.0",
|
48
48
|
"estraverse": "^4.2.0",
|
49
49
|
"esutils": "^2.0.2",
|
@@ -64,7 +64,7 @@
|
|
64
64
|
"natural-compare": "^1.4.0",
|
65
65
|
"optionator": "^0.8.2",
|
66
66
|
"path-is-inside": "^1.0.2",
|
67
|
-
"pluralize": "^
|
67
|
+
"pluralize": "^7.0.0",
|
68
68
|
"progress": "^2.0.0",
|
69
69
|
"require-uncached": "^1.0.3",
|
70
70
|
"semver": "^5.3.0",
|
@@ -85,11 +85,11 @@
|
|
85
85
|
"coveralls": "^2.13.1",
|
86
86
|
"dateformat": "^2.0.0",
|
87
87
|
"ejs": "^2.5.6",
|
88
|
-
"eslint-plugin-eslint-plugin": "^
|
88
|
+
"eslint-plugin-eslint-plugin": "^1.2.0",
|
89
89
|
"eslint-plugin-node": "^5.1.0",
|
90
90
|
"eslint-release": "^0.10.1",
|
91
91
|
"eslump": "1.6.0",
|
92
|
-
"esprima": "^
|
92
|
+
"esprima": "^4.0.0",
|
93
93
|
"esprima-fb": "^15001.1001.0-dev-harmony-fb",
|
94
94
|
"istanbul": "^0.4.5",
|
95
95
|
"jsdoc": "^3.4.3",
|
@@ -100,7 +100,7 @@
|
|
100
100
|
"karma-phantomjs-launcher": "^1.0.4",
|
101
101
|
"leche": "^2.1.2",
|
102
102
|
"load-perf": "^0.2.0",
|
103
|
-
"markdownlint": "^0.
|
103
|
+
"markdownlint": "^0.6.1",
|
104
104
|
"mocha": "^3.4.2",
|
105
105
|
"mock-fs": "^4.3.0",
|
106
106
|
"npm-license": "^0.3.3",
|
@@ -108,7 +108,7 @@
|
|
108
108
|
"proxyquire": "^1.8.0",
|
109
109
|
"shelljs": "^0.7.7",
|
110
110
|
"shelljs-nodecli": "~0.1.1",
|
111
|
-
"sinon": "^
|
111
|
+
"sinon": "^3.2.1",
|
112
112
|
"temp": "^0.8.3",
|
113
113
|
"through": "^2.3.8"
|
114
114
|
},
|
package/lib/rule-context.js
DELETED
@@ -1,241 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview RuleContext utility for rules
|
3
|
-
* @author Nicholas C. Zakas
|
4
|
-
*/
|
5
|
-
"use strict";
|
6
|
-
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Requirements
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
const assert = require("assert");
|
12
|
-
const ruleFixer = require("./util/rule-fixer");
|
13
|
-
|
14
|
-
//------------------------------------------------------------------------------
|
15
|
-
// Constants
|
16
|
-
//------------------------------------------------------------------------------
|
17
|
-
|
18
|
-
const PASSTHROUGHS = [
|
19
|
-
"getAncestors",
|
20
|
-
"getDeclaredVariables",
|
21
|
-
"getFilename",
|
22
|
-
"getScope",
|
23
|
-
"getSourceCode",
|
24
|
-
"markVariableAsUsed",
|
25
|
-
|
26
|
-
// DEPRECATED
|
27
|
-
"getAllComments",
|
28
|
-
"getComments",
|
29
|
-
"getFirstToken",
|
30
|
-
"getFirstTokens",
|
31
|
-
"getJSDocComment",
|
32
|
-
"getLastToken",
|
33
|
-
"getLastTokens",
|
34
|
-
"getNodeByRangeIndex",
|
35
|
-
"getSource",
|
36
|
-
"getSourceLines",
|
37
|
-
"getTokenAfter",
|
38
|
-
"getTokenBefore",
|
39
|
-
"getTokenByRangeStart",
|
40
|
-
"getTokens",
|
41
|
-
"getTokensAfter",
|
42
|
-
"getTokensBefore",
|
43
|
-
"getTokensBetween"
|
44
|
-
];
|
45
|
-
|
46
|
-
//------------------------------------------------------------------------------
|
47
|
-
// Typedefs
|
48
|
-
//------------------------------------------------------------------------------
|
49
|
-
|
50
|
-
/**
|
51
|
-
* An error message description
|
52
|
-
* @typedef {Object} MessageDescriptor
|
53
|
-
* @property {string} nodeType The type of node.
|
54
|
-
* @property {Location} loc The location of the problem.
|
55
|
-
* @property {string} message The problem message.
|
56
|
-
* @property {Object} [data] Optional data to use to fill in placeholders in the
|
57
|
-
* message.
|
58
|
-
* @property {Function} fix The function to call that creates a fix command.
|
59
|
-
*/
|
60
|
-
|
61
|
-
//------------------------------------------------------------------------------
|
62
|
-
// Module Definition
|
63
|
-
//------------------------------------------------------------------------------
|
64
|
-
|
65
|
-
/**
|
66
|
-
* Compares items in a fixes array by range.
|
67
|
-
* @param {Fix} a The first message.
|
68
|
-
* @param {Fix} b The second message.
|
69
|
-
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
70
|
-
* @private
|
71
|
-
*/
|
72
|
-
function compareFixesByRange(a, b) {
|
73
|
-
return a.range[0] - b.range[0] || a.range[1] - b.range[1];
|
74
|
-
}
|
75
|
-
|
76
|
-
/**
|
77
|
-
* Merges the given fixes array into one.
|
78
|
-
* @param {Fix[]} fixes The fixes to merge.
|
79
|
-
* @param {SourceCode} sourceCode The source code object to get the text between fixes.
|
80
|
-
* @returns {void}
|
81
|
-
*/
|
82
|
-
function mergeFixes(fixes, sourceCode) {
|
83
|
-
if (fixes.length === 0) {
|
84
|
-
return null;
|
85
|
-
}
|
86
|
-
if (fixes.length === 1) {
|
87
|
-
return fixes[0];
|
88
|
-
}
|
89
|
-
|
90
|
-
fixes.sort(compareFixesByRange);
|
91
|
-
|
92
|
-
const originalText = sourceCode.text;
|
93
|
-
const start = fixes[0].range[0];
|
94
|
-
const end = fixes[fixes.length - 1].range[1];
|
95
|
-
let text = "";
|
96
|
-
let lastPos = Number.MIN_SAFE_INTEGER;
|
97
|
-
|
98
|
-
for (const fix of fixes) {
|
99
|
-
assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");
|
100
|
-
|
101
|
-
if (fix.range[0] >= 0) {
|
102
|
-
text += originalText.slice(Math.max(0, start, lastPos), fix.range[0]);
|
103
|
-
}
|
104
|
-
text += fix.text;
|
105
|
-
lastPos = fix.range[1];
|
106
|
-
}
|
107
|
-
text += originalText.slice(Math.max(0, start, lastPos), end);
|
108
|
-
|
109
|
-
return { range: [start, end], text };
|
110
|
-
}
|
111
|
-
|
112
|
-
/**
|
113
|
-
* Gets one fix object from the given descriptor.
|
114
|
-
* If the descriptor retrieves multiple fixes, this merges those to one.
|
115
|
-
* @param {Object} descriptor The report descriptor.
|
116
|
-
* @param {SourceCode} sourceCode The source code object to get text between fixes.
|
117
|
-
* @returns {Fix} The got fix object.
|
118
|
-
*/
|
119
|
-
function getFix(descriptor, sourceCode) {
|
120
|
-
if (typeof descriptor.fix !== "function") {
|
121
|
-
return null;
|
122
|
-
}
|
123
|
-
|
124
|
-
// @type {null | Fix | Fix[] | IterableIterator<Fix>}
|
125
|
-
const fix = descriptor.fix(ruleFixer);
|
126
|
-
|
127
|
-
// Merge to one.
|
128
|
-
if (fix && Symbol.iterator in fix) {
|
129
|
-
return mergeFixes(Array.from(fix), sourceCode);
|
130
|
-
}
|
131
|
-
return fix;
|
132
|
-
}
|
133
|
-
|
134
|
-
/**
|
135
|
-
* Rule context class
|
136
|
-
* Acts as an abstraction layer between rules and the main linter object.
|
137
|
-
*/
|
138
|
-
class RuleContext {
|
139
|
-
|
140
|
-
/**
|
141
|
-
* @param {string} ruleId The ID of the rule using this object.
|
142
|
-
* @param {Linter} linter The linter object.
|
143
|
-
* @param {number} severity The configured severity level of the rule.
|
144
|
-
* @param {Array} options The configuration information to be added to the rule.
|
145
|
-
* @param {Object} settings The configuration settings passed from the config file.
|
146
|
-
* @param {Object} parserOptions The parserOptions settings passed from the config file.
|
147
|
-
* @param {Object} parserPath The parser setting passed from the config file.
|
148
|
-
* @param {Object} meta The metadata of the rule
|
149
|
-
* @param {Object} parserServices The parser services for the rule.
|
150
|
-
*/
|
151
|
-
constructor(ruleId, linter, severity, options, settings, parserOptions, parserPath, meta, parserServices) {
|
152
|
-
|
153
|
-
// public.
|
154
|
-
this.id = ruleId;
|
155
|
-
this.options = options;
|
156
|
-
this.settings = settings;
|
157
|
-
this.parserOptions = parserOptions;
|
158
|
-
this.parserPath = parserPath;
|
159
|
-
this.meta = meta;
|
160
|
-
|
161
|
-
// create a separate copy and freeze it (it's not nice to freeze other people's objects)
|
162
|
-
this.parserServices = Object.freeze(Object.assign({}, parserServices));
|
163
|
-
|
164
|
-
// private.
|
165
|
-
this._linter = linter;
|
166
|
-
this._severity = severity;
|
167
|
-
|
168
|
-
Object.freeze(this);
|
169
|
-
}
|
170
|
-
|
171
|
-
/**
|
172
|
-
* Passthrough to Linter#report() that automatically assigns the rule ID and severity.
|
173
|
-
* @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
|
174
|
-
* descriptor.
|
175
|
-
* @param {Object=} location The location of the error.
|
176
|
-
* @param {string} message The message to display to the user.
|
177
|
-
* @param {Object} opts Optional template data which produces a formatted message
|
178
|
-
* with symbols being replaced by this object's values.
|
179
|
-
* @returns {void}
|
180
|
-
*/
|
181
|
-
report(nodeOrDescriptor, location, message, opts) {
|
182
|
-
|
183
|
-
// check to see if it's a new style call
|
184
|
-
if (arguments.length === 1) {
|
185
|
-
const descriptor = nodeOrDescriptor;
|
186
|
-
const fix = getFix(descriptor, this.getSourceCode());
|
187
|
-
|
188
|
-
if (descriptor.loc) {
|
189
|
-
this._linter.report(
|
190
|
-
this.id,
|
191
|
-
this._severity,
|
192
|
-
descriptor.node,
|
193
|
-
descriptor.loc,
|
194
|
-
descriptor.message,
|
195
|
-
descriptor.data,
|
196
|
-
fix,
|
197
|
-
this.meta
|
198
|
-
);
|
199
|
-
} else {
|
200
|
-
this._linter.report(
|
201
|
-
this.id,
|
202
|
-
this._severity,
|
203
|
-
descriptor.node,
|
204
|
-
|
205
|
-
/* loc not provided */
|
206
|
-
descriptor.message,
|
207
|
-
descriptor.data,
|
208
|
-
fix,
|
209
|
-
this.meta
|
210
|
-
);
|
211
|
-
}
|
212
|
-
|
213
|
-
} else {
|
214
|
-
|
215
|
-
// old style call
|
216
|
-
this._linter.report(
|
217
|
-
this.id,
|
218
|
-
this._severity,
|
219
|
-
nodeOrDescriptor,
|
220
|
-
location,
|
221
|
-
message,
|
222
|
-
opts,
|
223
|
-
this.meta
|
224
|
-
);
|
225
|
-
}
|
226
|
-
}
|
227
|
-
}
|
228
|
-
|
229
|
-
// Copy over passthrough methods.
|
230
|
-
PASSTHROUGHS.forEach(name => {
|
231
|
-
Object.defineProperty(RuleContext.prototype, name, {
|
232
|
-
value() {
|
233
|
-
return this._linter[name].apply(this._linter, arguments);
|
234
|
-
},
|
235
|
-
configurable: true,
|
236
|
-
writable: true,
|
237
|
-
enumerable: false
|
238
|
-
});
|
239
|
-
});
|
240
|
-
|
241
|
-
module.exports = RuleContext;
|