eslint 8.4.1 → 8.8.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/README.md +3 -3
- package/bin/eslint.js +7 -1
- package/lib/cli-engine/cli-engine.js +10 -1
- package/lib/eslint/eslint.js +6 -0
- package/lib/linter/apply-disable-directives.js +55 -60
- package/lib/linter/linter.js +90 -27
- package/lib/rule-tester/rule-tester.js +19 -5
- package/lib/rules/camelcase.js +7 -1
- package/lib/rules/id-match.js +35 -1
- package/lib/rules/index.js +1 -0
- package/lib/rules/keyword-spacing.js +32 -0
- package/lib/rules/no-constant-condition.js +23 -4
- package/lib/rules/no-invalid-this.js +50 -53
- package/lib/rules/no-restricted-exports.js +9 -3
- package/lib/rules/no-restricted-imports.js +24 -7
- package/lib/rules/no-restricted-modules.js +2 -1
- package/lib/rules/no-self-assign.js +1 -1
- package/lib/rules/no-useless-rename.js +8 -4
- package/lib/rules/prefer-object-has-own.js +112 -0
- package/lib/rules/prefer-regex-literals.js +217 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/quotes.js +12 -1
- package/lib/rules/utils/ast-utils.js +21 -1
- package/lib/shared/types.js +15 -0
- package/messages/no-config-found.js +1 -1
- package/package.json +7 -7
- package/lib/init/autoconfig.js +0 -351
- package/lib/init/config-file.js +0 -144
- package/lib/init/config-initializer.js +0 -709
- package/lib/init/config-rule.js +0 -316
- package/lib/init/npm-utils.js +0 -179
- package/lib/init/source-code-utils.js +0 -110
package/README.md
CHANGED
@@ -54,7 +54,7 @@ $ npm install eslint --save-dev
|
|
54
54
|
You should then set up a configuration file:
|
55
55
|
|
56
56
|
```sh
|
57
|
-
$
|
57
|
+
$ npm init @eslint/config
|
58
58
|
```
|
59
59
|
|
60
60
|
After that, you can run ESLint on any file or directory like this:
|
@@ -65,7 +65,7 @@ $ ./node_modules/.bin/eslint yourfile.js
|
|
65
65
|
|
66
66
|
## <a name="configuration"></a>Configuration
|
67
67
|
|
68
|
-
After running `
|
68
|
+
After running `npm init @eslint/config`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
|
69
69
|
|
70
70
|
```json
|
71
71
|
{
|
@@ -294,7 +294,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
294
294
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
295
295
|
<p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://coinbase.com"><img src="https://avatars.githubusercontent.com/u/1885080?v=4" alt="Coinbase" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
|
296
296
|
<p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
|
297
|
-
<p><a href="https://
|
297
|
+
<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
|
298
298
|
<!--sponsorsend-->
|
299
299
|
|
300
300
|
## <a name="technology-sponsors"></a>Technology Sponsors
|
package/bin/eslint.js
CHANGED
@@ -124,7 +124,13 @@ ${message}`);
|
|
124
124
|
|
125
125
|
// Call the config initializer if `--init` is present.
|
126
126
|
if (process.argv.includes("--init")) {
|
127
|
-
|
127
|
+
|
128
|
+
// `eslint --init` has been moved to `@eslint/create-config`
|
129
|
+
console.warn("You can also run this command directly using 'npm init @eslint/config'.");
|
130
|
+
|
131
|
+
const spawn = require("cross-spawn");
|
132
|
+
|
133
|
+
spawn.sync("npm", ["init", "@eslint/config"], { encoding: "utf8", stdio: "inherit" });
|
128
134
|
return;
|
129
135
|
}
|
130
136
|
|
@@ -51,6 +51,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
|
51
51
|
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
52
52
|
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
|
53
53
|
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
54
|
+
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
54
55
|
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
|
55
56
|
/** @typedef {import("../shared/types").Plugin} Plugin */
|
56
57
|
/** @typedef {import("../shared/types").RuleConf} RuleConf */
|
@@ -91,7 +92,9 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
|
91
92
|
* @typedef {Object} LintResult
|
92
93
|
* @property {string} filePath The path to the file that was linted.
|
93
94
|
* @property {LintMessage[]} messages All of the messages for the result.
|
95
|
+
* @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
|
94
96
|
* @property {number} errorCount Number of errors for the result.
|
97
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
95
98
|
* @property {number} warningCount Number of warnings for the result.
|
96
99
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
97
100
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -104,6 +107,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
|
104
107
|
* @typedef {Object} LintReport
|
105
108
|
* @property {LintResult[]} results All of the result.
|
106
109
|
* @property {number} errorCount Number of errors for the result.
|
110
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
107
111
|
* @property {number} warningCount Number of warnings for the result.
|
108
112
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
109
113
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -261,6 +265,7 @@ function verifyText({
|
|
261
265
|
const result = {
|
262
266
|
filePath,
|
263
267
|
messages,
|
268
|
+
suppressedMessages: linter.getSuppressedMessages(),
|
264
269
|
...calculateStatsPerFile(messages)
|
265
270
|
};
|
266
271
|
|
@@ -307,7 +312,9 @@ function createIgnoreResult(filePath, baseDir) {
|
|
307
312
|
message
|
308
313
|
}
|
309
314
|
],
|
315
|
+
suppressedMessages: [],
|
310
316
|
errorCount: 0,
|
317
|
+
fatalErrorCount: 0,
|
311
318
|
warningCount: 1,
|
312
319
|
fixableErrorCount: 0,
|
313
320
|
fixableWarningCount: 0
|
@@ -408,7 +415,7 @@ function isErrorMessage(message) {
|
|
408
415
|
* a directory or looks like a directory (ends in `path.sep`), in which case the file
|
409
416
|
* name will be the `cacheFile/.cache_hashOfCWD`
|
410
417
|
*
|
411
|
-
* if cacheFile points to a file or looks like a file then
|
418
|
+
* if cacheFile points to a file or looks like a file then it will just use that file
|
412
419
|
* @param {string} cacheFile The name of file to be used to store the cache
|
413
420
|
* @param {string} cwd Current working directory
|
414
421
|
* @returns {string} the resolved path to the cache file
|
@@ -680,11 +687,13 @@ class CLIEngine {
|
|
680
687
|
|
681
688
|
results.forEach(result => {
|
682
689
|
const filteredMessages = result.messages.filter(isErrorMessage);
|
690
|
+
const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
|
683
691
|
|
684
692
|
if (filteredMessages.length > 0) {
|
685
693
|
filtered.push({
|
686
694
|
...result,
|
687
695
|
messages: filteredMessages,
|
696
|
+
suppressedMessages: filteredSuppressedMessages,
|
688
697
|
errorCount: filteredMessages.length,
|
689
698
|
warningCount: 0,
|
690
699
|
fixableErrorCount: result.fixableErrorCount,
|
package/lib/eslint/eslint.js
CHANGED
@@ -32,6 +32,7 @@ const { version } = require("../../package.json");
|
|
32
32
|
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
|
33
33
|
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
34
34
|
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
35
|
+
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
35
36
|
/** @typedef {import("../shared/types").Plugin} Plugin */
|
36
37
|
/** @typedef {import("../shared/types").Rule} Rule */
|
37
38
|
|
@@ -78,7 +79,9 @@ const { version } = require("../../package.json");
|
|
78
79
|
* @typedef {Object} LintResult
|
79
80
|
* @property {string} filePath The path to the file that was linted.
|
80
81
|
* @property {LintMessage[]} messages All of the messages for the result.
|
82
|
+
* @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
|
81
83
|
* @property {number} errorCount Number of errors for the result.
|
84
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
82
85
|
* @property {number} warningCount Number of warnings for the result.
|
83
86
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
84
87
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -525,6 +528,9 @@ class ESLint {
|
|
525
528
|
for (const { ruleId } of result.messages) {
|
526
529
|
resultRuleIds.add(ruleId);
|
527
530
|
}
|
531
|
+
for (const { ruleId } of result.suppressedMessages) {
|
532
|
+
resultRuleIds.add(ruleId);
|
533
|
+
}
|
528
534
|
}
|
529
535
|
|
530
536
|
// create a map of all rules in the results
|
@@ -43,7 +43,7 @@ function groupByParentComment(directives) {
|
|
43
43
|
* Creates removal details for a set of directives within the same comment.
|
44
44
|
* @param {Directive[]} directives Unused directives to be removed.
|
45
45
|
* @param {Token} commentToken The backing Comment token.
|
46
|
-
* @returns {{ description, fix,
|
46
|
+
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
47
47
|
*/
|
48
48
|
function createIndividualDirectivesRemoval(directives, commentToken) {
|
49
49
|
|
@@ -138,7 +138,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
138
138
|
],
|
139
139
|
text: ""
|
140
140
|
},
|
141
|
-
|
141
|
+
unprocessedDirective: directive.unprocessedDirective
|
142
142
|
};
|
143
143
|
});
|
144
144
|
}
|
@@ -147,7 +147,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
147
147
|
* Creates a description of deleting an entire unused disable comment.
|
148
148
|
* @param {Directive[]} directives Unused directives to be removed.
|
149
149
|
* @param {Token} commentToken The backing Comment token.
|
150
|
-
* @returns {{ description, fix,
|
150
|
+
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
|
151
151
|
*/
|
152
152
|
function createCommentRemoval(directives, commentToken) {
|
153
153
|
const { range } = commentToken;
|
@@ -161,14 +161,14 @@ function createCommentRemoval(directives, commentToken) {
|
|
161
161
|
range,
|
162
162
|
text: " "
|
163
163
|
},
|
164
|
-
|
164
|
+
unprocessedDirective: directives[0].unprocessedDirective
|
165
165
|
};
|
166
166
|
}
|
167
167
|
|
168
168
|
/**
|
169
169
|
* Parses details from directives to create output Problems.
|
170
170
|
* @param {Directive[]} allDirectives Unused directives to be removed.
|
171
|
-
* @returns {{ description, fix,
|
171
|
+
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
172
172
|
*/
|
173
173
|
function processUnusedDisableDirectives(allDirectives) {
|
174
174
|
const directiveGroups = groupByParentComment(allDirectives);
|
@@ -197,62 +197,52 @@ function processUnusedDisableDirectives(allDirectives) {
|
|
197
197
|
* for the exported function, except that `reportUnusedDisableDirectives` is not supported
|
198
198
|
* (this function always reports unused disable directives).
|
199
199
|
* @returns {{problems: Problem[], unusedDisableDirectives: Problem[]}} An object with a list
|
200
|
-
* of
|
200
|
+
* of problems (including suppressed ones) and unused eslint-disable directives
|
201
201
|
*/
|
202
202
|
function applyDirectives(options) {
|
203
203
|
const problems = [];
|
204
|
-
let nextDirectiveIndex = 0;
|
205
|
-
let currentGlobalDisableDirective = null;
|
206
|
-
const disabledRuleMap = new Map();
|
207
|
-
|
208
|
-
// enabledRules is only used when there is a current global disable directive.
|
209
|
-
const enabledRules = new Set();
|
210
204
|
const usedDisableDirectives = new Set();
|
211
205
|
|
212
206
|
for (const problem of options.problems) {
|
207
|
+
let disableDirectivesForProblem = [];
|
208
|
+
let nextDirectiveIndex = 0;
|
209
|
+
|
213
210
|
while (
|
214
211
|
nextDirectiveIndex < options.directives.length &&
|
215
212
|
compareLocations(options.directives[nextDirectiveIndex], problem) <= 0
|
216
213
|
) {
|
217
214
|
const directive = options.directives[nextDirectiveIndex++];
|
218
215
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
break;
|
232
|
-
|
233
|
-
case "enable":
|
234
|
-
if (directive.ruleId === null) {
|
235
|
-
currentGlobalDisableDirective = null;
|
236
|
-
disabledRuleMap.clear();
|
237
|
-
} else if (currentGlobalDisableDirective) {
|
238
|
-
enabledRules.add(directive.ruleId);
|
239
|
-
disabledRuleMap.delete(directive.ruleId);
|
240
|
-
} else {
|
241
|
-
disabledRuleMap.delete(directive.ruleId);
|
242
|
-
}
|
243
|
-
break;
|
244
|
-
|
245
|
-
// no default
|
216
|
+
if (directive.ruleId === null || directive.ruleId === problem.ruleId) {
|
217
|
+
switch (directive.type) {
|
218
|
+
case "disable":
|
219
|
+
disableDirectivesForProblem.push(directive);
|
220
|
+
break;
|
221
|
+
|
222
|
+
case "enable":
|
223
|
+
disableDirectivesForProblem = [];
|
224
|
+
break;
|
225
|
+
|
226
|
+
// no default
|
227
|
+
}
|
246
228
|
}
|
247
229
|
}
|
248
230
|
|
249
|
-
if (
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
231
|
+
if (disableDirectivesForProblem.length > 0) {
|
232
|
+
const suppressions = disableDirectivesForProblem.map(directive => ({
|
233
|
+
kind: "directive",
|
234
|
+
justification: directive.unprocessedDirective.justification
|
235
|
+
}));
|
236
|
+
|
237
|
+
if (problem.suppressions) {
|
238
|
+
problem.suppressions = problem.suppressions.concat(suppressions);
|
239
|
+
} else {
|
240
|
+
problem.suppressions = suppressions;
|
241
|
+
usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
|
242
|
+
}
|
255
243
|
}
|
244
|
+
|
245
|
+
problems.push(problem);
|
256
246
|
}
|
257
247
|
|
258
248
|
const unusedDisableDirectivesToReport = options.directives
|
@@ -261,30 +251,35 @@ function applyDirectives(options) {
|
|
261
251
|
const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
|
262
252
|
|
263
253
|
const unusedDisableDirectives = processed
|
264
|
-
.map(({ description, fix,
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
:
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
254
|
+
.map(({ description, fix, unprocessedDirective }) => {
|
255
|
+
const { parentComment, type, line, column } = unprocessedDirective;
|
256
|
+
|
257
|
+
return {
|
258
|
+
ruleId: null,
|
259
|
+
message: description
|
260
|
+
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
261
|
+
: "Unused eslint-disable directive (no problems were reported).",
|
262
|
+
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
|
263
|
+
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
|
264
|
+
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
265
|
+
nodeType: null,
|
266
|
+
...options.disableFixes ? {} : { fix }
|
267
|
+
};
|
268
|
+
});
|
275
269
|
|
276
270
|
return { problems, unusedDisableDirectives };
|
277
271
|
}
|
278
272
|
|
279
273
|
/**
|
280
274
|
* Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
|
281
|
-
* of reported problems,
|
275
|
+
* of reported problems, adds the suppression information to the problems.
|
282
276
|
* @param {Object} options Information about directives and problems
|
283
277
|
* @param {{
|
284
278
|
* type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
285
279
|
* ruleId: (string|null),
|
286
280
|
* line: number,
|
287
|
-
* column: number
|
281
|
+
* column: number,
|
282
|
+
* justification: string
|
288
283
|
* }} options.directives Directive comments found in the file, with one-based columns.
|
289
284
|
* Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable
|
290
285
|
* comment for two different rules is represented as two directives).
|
@@ -292,8 +287,8 @@ function applyDirectives(options) {
|
|
292
287
|
* A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
|
293
288
|
* @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
|
294
289
|
* @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
|
295
|
-
* @returns {{ruleId: (string|null), line: number, column: number}[]}
|
296
|
-
*
|
290
|
+
* @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
|
291
|
+
* An object with a list of reported problems, the suppressed of which contain the suppression information.
|
297
292
|
*/
|
298
293
|
module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
|
299
294
|
const blockDirectives = directives
|
package/lib/linter/linter.js
CHANGED
@@ -59,6 +59,7 @@ const globals = require("../../conf/globals");
|
|
59
59
|
/** @typedef {import("../shared/types").Environment} Environment */
|
60
60
|
/** @typedef {import("../shared/types").GlobalConf} GlobalConf */
|
61
61
|
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
62
|
+
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
62
63
|
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
|
63
64
|
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
|
64
65
|
/** @typedef {import("../shared/types").Processor} Processor */
|
@@ -77,6 +78,7 @@ const globals = require("../../conf/globals");
|
|
77
78
|
* @property {number} line The line number
|
78
79
|
* @property {number} column The column number
|
79
80
|
* @property {(string|null)} ruleId The rule ID
|
81
|
+
* @property {string} justification The justification of directive
|
80
82
|
*/
|
81
83
|
|
82
84
|
/**
|
@@ -84,6 +86,7 @@ const globals = require("../../conf/globals");
|
|
84
86
|
* @typedef {Object} LinterInternalSlots
|
85
87
|
* @property {ConfigArray|null} lastConfigArray The `ConfigArray` instance that the last `verify()` call used.
|
86
88
|
* @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
|
89
|
+
* @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
|
87
90
|
* @property {Map<string, Parser>} parserMap The loaded parsers.
|
88
91
|
* @property {Rules} ruleMap The loaded rules.
|
89
92
|
*/
|
@@ -287,11 +290,12 @@ function createLintingProblem(options) {
|
|
287
290
|
* @param {token} options.commentToken The Comment token
|
288
291
|
* @param {string} options.value The value after the directive in the comment
|
289
292
|
* comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
|
293
|
+
* @param {string} options.justification The justification of the directive
|
290
294
|
* @param {function(string): {create: Function}} options.ruleMapper A map from rule IDs to defined rules
|
291
295
|
* @returns {Object} Directives and problems from the comment
|
292
296
|
*/
|
293
297
|
function createDisableDirectives(options) {
|
294
|
-
const { commentToken, type, value, ruleMapper } = options;
|
298
|
+
const { commentToken, type, value, justification, ruleMapper } = options;
|
295
299
|
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
296
300
|
const directiveRules = ruleIds.length ? ruleIds : [null];
|
297
301
|
const result = {
|
@@ -305,7 +309,25 @@ function createDisableDirectives(options) {
|
|
305
309
|
|
306
310
|
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
307
311
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
308
|
-
|
312
|
+
if (type === "disable-next-line") {
|
313
|
+
result.directives.push({
|
314
|
+
parentComment,
|
315
|
+
type,
|
316
|
+
line: commentToken.loc.end.line,
|
317
|
+
column: commentToken.loc.end.column + 1,
|
318
|
+
ruleId,
|
319
|
+
justification
|
320
|
+
});
|
321
|
+
} else {
|
322
|
+
result.directives.push({
|
323
|
+
parentComment,
|
324
|
+
type,
|
325
|
+
line: commentToken.loc.start.line,
|
326
|
+
column: commentToken.loc.start.column + 1,
|
327
|
+
ruleId,
|
328
|
+
justification
|
329
|
+
});
|
330
|
+
}
|
309
331
|
} else {
|
310
332
|
result.directiveProblems.push(createLintingProblem({ ruleId, loc: commentToken.loc }));
|
311
333
|
}
|
@@ -314,26 +336,34 @@ function createDisableDirectives(options) {
|
|
314
336
|
}
|
315
337
|
|
316
338
|
/**
|
317
|
-
*
|
318
|
-
* @param {string} value The comment text to
|
319
|
-
* @returns {string} The
|
339
|
+
* Extract the directive and the justification from a given directive comment and trim them.
|
340
|
+
* @param {string} value The comment text to extract.
|
341
|
+
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
|
320
342
|
*/
|
321
|
-
function
|
322
|
-
|
343
|
+
function extractDirectiveComment(value) {
|
344
|
+
const match = /\s-{2,}\s/u.exec(value);
|
345
|
+
|
346
|
+
if (!match) {
|
347
|
+
return { directivePart: value.trim(), justificationPart: "" };
|
348
|
+
}
|
349
|
+
|
350
|
+
const directive = value.slice(0, match.index).trim();
|
351
|
+
const justification = value.slice(match.index + match[0].length).trim();
|
352
|
+
|
353
|
+
return { directivePart: directive, justificationPart: justification };
|
323
354
|
}
|
324
355
|
|
325
356
|
/**
|
326
357
|
* Parses comments in file to extract file-specific config of rules, globals
|
327
358
|
* and environments and merges them with global config; also code blocks
|
328
359
|
* where reporting is disabled or enabled and merges them with reporting config.
|
329
|
-
* @param {string} filename The file being checked.
|
330
360
|
* @param {ASTNode} ast The top node of the AST.
|
331
361
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
332
362
|
* @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
|
333
363
|
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
|
334
364
|
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
335
365
|
*/
|
336
|
-
function getDirectiveComments(
|
366
|
+
function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
|
337
367
|
const configuredRules = {};
|
338
368
|
const enabledGlobals = Object.create(null);
|
339
369
|
const exportedVariables = {};
|
@@ -344,8 +374,9 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
|
344
374
|
});
|
345
375
|
|
346
376
|
ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
|
347
|
-
const
|
348
|
-
|
377
|
+
const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
|
378
|
+
|
379
|
+
const match = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u.exec(directivePart);
|
349
380
|
|
350
381
|
if (!match) {
|
351
382
|
return;
|
@@ -369,7 +400,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
|
369
400
|
return;
|
370
401
|
}
|
371
402
|
|
372
|
-
if (
|
403
|
+
if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
373
404
|
const message = `${directiveText} comment should not span multiple lines.`;
|
374
405
|
|
375
406
|
problems.push(createLintingProblem({
|
@@ -380,7 +411,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
|
380
411
|
return;
|
381
412
|
}
|
382
413
|
|
383
|
-
const directiveValue =
|
414
|
+
const directiveValue = directivePart.slice(match.index + directiveText.length);
|
384
415
|
|
385
416
|
switch (directiveText) {
|
386
417
|
case "eslint-disable":
|
@@ -388,7 +419,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
|
388
419
|
case "eslint-disable-next-line":
|
389
420
|
case "eslint-disable-line": {
|
390
421
|
const directiveType = directiveText.slice("eslint-".length);
|
391
|
-
const options = { commentToken: comment, type: directiveType, value: directiveValue, ruleMapper };
|
422
|
+
const options = { commentToken: comment, type: directiveType, value: directiveValue, justification: justificationPart, ruleMapper };
|
392
423
|
const { directives, directiveProblems } = createDisableDirectives(options);
|
393
424
|
|
394
425
|
disableDirectives.push(...directives);
|
@@ -545,7 +576,7 @@ function findEslintEnv(text) {
|
|
545
576
|
if (match[0].endsWith("*/")) {
|
546
577
|
retv = Object.assign(
|
547
578
|
retv || {},
|
548
|
-
commentParser.parseListConfig(
|
579
|
+
commentParser.parseListConfig(extractDirectiveComment(match[1]).directivePart)
|
549
580
|
);
|
550
581
|
}
|
551
582
|
}
|
@@ -1220,6 +1251,7 @@ class Linter {
|
|
1220
1251
|
cwd: normalizeCwd(cwd),
|
1221
1252
|
lastConfigArray: null,
|
1222
1253
|
lastSourceCode: null,
|
1254
|
+
lastSuppressedMessages: [],
|
1223
1255
|
configType, // TODO: Remove after flat config conversion
|
1224
1256
|
parserMap: new Map([["espree", espree]]),
|
1225
1257
|
ruleMap: new Rules()
|
@@ -1243,7 +1275,7 @@ class Linter {
|
|
1243
1275
|
* @param {ConfigData} providedConfig An ESLintConfig instance to configure everything.
|
1244
1276
|
* @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
|
1245
1277
|
* @throws {Error} If during rule execution.
|
1246
|
-
* @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
|
1278
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
|
1247
1279
|
*/
|
1248
1280
|
_verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
|
1249
1281
|
const slots = internalSlotsMap.get(this);
|
@@ -1332,7 +1364,7 @@ class Linter {
|
|
1332
1364
|
|
1333
1365
|
const sourceCode = slots.lastSourceCode;
|
1334
1366
|
const commentDirectives = options.allowInlineConfig
|
1335
|
-
? getDirectiveComments(
|
1367
|
+
? getDirectiveComments(sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
|
1336
1368
|
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1337
1369
|
|
1338
1370
|
// augment global scope with declared global variables
|
@@ -1425,11 +1457,11 @@ class Linter {
|
|
1425
1457
|
configArray.normalizeSync();
|
1426
1458
|
}
|
1427
1459
|
|
1428
|
-
return this._verifyWithFlatConfigArray(textOrSourceCode, configArray, options, true);
|
1460
|
+
return this._distinguishSuppressedMessages(this._verifyWithFlatConfigArray(textOrSourceCode, configArray, options, true));
|
1429
1461
|
}
|
1430
1462
|
|
1431
1463
|
if (typeof config.extractConfig === "function") {
|
1432
|
-
return this._verifyWithConfigArray(textOrSourceCode, config, options);
|
1464
|
+
return this._distinguishSuppressedMessages(this._verifyWithConfigArray(textOrSourceCode, config, options));
|
1433
1465
|
}
|
1434
1466
|
}
|
1435
1467
|
|
@@ -1443,9 +1475,9 @@ class Linter {
|
|
1443
1475
|
* So we cannot apply multiple processors.
|
1444
1476
|
*/
|
1445
1477
|
if (options.preprocess || options.postprocess) {
|
1446
|
-
return this._verifyWithProcessor(textOrSourceCode, config, options);
|
1478
|
+
return this._distinguishSuppressedMessages(this._verifyWithProcessor(textOrSourceCode, config, options));
|
1447
1479
|
}
|
1448
|
-
return this._verifyWithoutProcessors(textOrSourceCode, config, options);
|
1480
|
+
return this._distinguishSuppressedMessages(this._verifyWithoutProcessors(textOrSourceCode, config, options));
|
1449
1481
|
}
|
1450
1482
|
|
1451
1483
|
/**
|
@@ -1454,7 +1486,7 @@ class Linter {
|
|
1454
1486
|
* @param {FlatConfig} config The config array.
|
1455
1487
|
* @param {VerifyOptions&ProcessorOptions} options The options.
|
1456
1488
|
* @param {FlatConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively.
|
1457
|
-
* @returns {LintMessage[]} The found problems.
|
1489
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
1458
1490
|
*/
|
1459
1491
|
_verifyWithFlatConfigArrayAndProcessor(textOrSourceCode, config, options, configForRecursive) {
|
1460
1492
|
const filename = options.filename || "<input>";
|
@@ -1511,7 +1543,7 @@ class Linter {
|
|
1511
1543
|
* @param {FlatConfig} providedConfig An ESLintConfig instance to configure everything.
|
1512
1544
|
* @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
|
1513
1545
|
* @throws {Error} If during rule execution.
|
1514
|
-
* @returns {LintMessage[]} The results as an array of messages or an empty array if no messages.
|
1546
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
|
1515
1547
|
*/
|
1516
1548
|
_verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
|
1517
1549
|
const slots = internalSlotsMap.get(this);
|
@@ -1593,7 +1625,6 @@ class Linter {
|
|
1593
1625
|
const sourceCode = slots.lastSourceCode;
|
1594
1626
|
const commentDirectives = options.allowInlineConfig
|
1595
1627
|
? getDirectiveComments(
|
1596
|
-
options.filename,
|
1597
1628
|
sourceCode.ast,
|
1598
1629
|
ruleId => getRuleFromConfig(ruleId, config),
|
1599
1630
|
options.warnInlineConfig
|
@@ -1661,7 +1692,7 @@ class Linter {
|
|
1661
1692
|
* @param {string|SourceCode} textOrSourceCode The source code.
|
1662
1693
|
* @param {ConfigArray} configArray The config array.
|
1663
1694
|
* @param {VerifyOptions&ProcessorOptions} options The options.
|
1664
|
-
* @returns {LintMessage[]} The found problems.
|
1695
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
1665
1696
|
*/
|
1666
1697
|
_verifyWithConfigArray(textOrSourceCode, configArray, options) {
|
1667
1698
|
debug("With ConfigArray: %s", options.filename);
|
@@ -1698,7 +1729,7 @@ class Linter {
|
|
1698
1729
|
* @param {VerifyOptions&ProcessorOptions} options The options.
|
1699
1730
|
* @param {boolean} [firstCall=false] Indicates if this is being called directly
|
1700
1731
|
* from verify(). (TODO: Remove once eslintrc is removed.)
|
1701
|
-
* @returns {LintMessage[]} The found problems.
|
1732
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
1702
1733
|
*/
|
1703
1734
|
_verifyWithFlatConfigArray(textOrSourceCode, configArray, options, firstCall = false) {
|
1704
1735
|
debug("With flat config: %s", options.filename);
|
@@ -1738,7 +1769,7 @@ class Linter {
|
|
1738
1769
|
* @param {ConfigData|ExtractedConfig} config The config array.
|
1739
1770
|
* @param {VerifyOptions&ProcessorOptions} options The options.
|
1740
1771
|
* @param {ConfigArray} [configForRecursive] The `ConfigArray` object to apply multiple processors recursively.
|
1741
|
-
* @returns {LintMessage[]} The found problems.
|
1772
|
+
* @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
|
1742
1773
|
*/
|
1743
1774
|
_verifyWithProcessor(textOrSourceCode, config, options, configForRecursive) {
|
1744
1775
|
const filename = options.filename || "<input>";
|
@@ -1790,6 +1821,30 @@ class Linter {
|
|
1790
1821
|
return postprocess(messageLists, filenameToExpose);
|
1791
1822
|
}
|
1792
1823
|
|
1824
|
+
/**
|
1825
|
+
* Given a list of reported problems, distinguish problems between normal messages and suppressed messages.
|
1826
|
+
* The normal messages will be returned and the suppressed messages will be stored as lastSuppressedMessages.
|
1827
|
+
* @param {Problem[]} problems A list of reported problems.
|
1828
|
+
* @returns {LintMessage[]} A list of LintMessage.
|
1829
|
+
*/
|
1830
|
+
_distinguishSuppressedMessages(problems) {
|
1831
|
+
const messages = [];
|
1832
|
+
const suppressedMessages = [];
|
1833
|
+
const slots = internalSlotsMap.get(this);
|
1834
|
+
|
1835
|
+
for (const problem of problems) {
|
1836
|
+
if (problem.suppressions) {
|
1837
|
+
suppressedMessages.push(problem);
|
1838
|
+
} else {
|
1839
|
+
messages.push(problem);
|
1840
|
+
}
|
1841
|
+
}
|
1842
|
+
|
1843
|
+
slots.lastSuppressedMessages = suppressedMessages;
|
1844
|
+
|
1845
|
+
return messages;
|
1846
|
+
}
|
1847
|
+
|
1793
1848
|
/**
|
1794
1849
|
* Gets the SourceCode object representing the parsed source.
|
1795
1850
|
* @returns {SourceCode} The SourceCode object.
|
@@ -1798,6 +1853,14 @@ class Linter {
|
|
1798
1853
|
return internalSlotsMap.get(this).lastSourceCode;
|
1799
1854
|
}
|
1800
1855
|
|
1856
|
+
/**
|
1857
|
+
* Gets the list of SuppressedLintMessage produced in the last running.
|
1858
|
+
* @returns {SuppressedLintMessage[]} The list of SuppressedLintMessage
|
1859
|
+
*/
|
1860
|
+
getSuppressedMessages() {
|
1861
|
+
return internalSlotsMap.get(this).lastSuppressedMessages;
|
1862
|
+
}
|
1863
|
+
|
1801
1864
|
/**
|
1802
1865
|
* Defines a new linting rule.
|
1803
1866
|
* @param {string} ruleId A unique rule identifier
|