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
@@ -11,11 +11,15 @@
|
|
11
11
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
13
|
const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("eslint-utils");
|
14
|
+
const { RegExpValidator, visitRegExpAST, RegExpParser } = require("regexpp");
|
15
|
+
const { canTokensBeAdjacent } = require("./utils/ast-utils");
|
14
16
|
|
15
17
|
//------------------------------------------------------------------------------
|
16
18
|
// Helpers
|
17
19
|
//------------------------------------------------------------------------------
|
18
20
|
|
21
|
+
const REGEXPP_LATEST_ECMA_VERSION = 2022;
|
22
|
+
|
19
23
|
/**
|
20
24
|
* Determines whether the given node is a string literal.
|
21
25
|
* @param {ASTNode} node Node to check.
|
@@ -43,6 +47,71 @@ function isStaticTemplateLiteral(node) {
|
|
43
47
|
return node.type === "TemplateLiteral" && node.expressions.length === 0;
|
44
48
|
}
|
45
49
|
|
50
|
+
const validPrecedingTokens = [
|
51
|
+
"(",
|
52
|
+
";",
|
53
|
+
"[",
|
54
|
+
",",
|
55
|
+
"=",
|
56
|
+
"+",
|
57
|
+
"*",
|
58
|
+
"-",
|
59
|
+
"?",
|
60
|
+
"~",
|
61
|
+
"%",
|
62
|
+
"**",
|
63
|
+
"!",
|
64
|
+
"typeof",
|
65
|
+
"instanceof",
|
66
|
+
"&&",
|
67
|
+
"||",
|
68
|
+
"??",
|
69
|
+
"return",
|
70
|
+
"...",
|
71
|
+
"delete",
|
72
|
+
"void",
|
73
|
+
"in",
|
74
|
+
"<",
|
75
|
+
">",
|
76
|
+
"<=",
|
77
|
+
">=",
|
78
|
+
"==",
|
79
|
+
"===",
|
80
|
+
"!=",
|
81
|
+
"!==",
|
82
|
+
"<<",
|
83
|
+
">>",
|
84
|
+
">>>",
|
85
|
+
"&",
|
86
|
+
"|",
|
87
|
+
"^",
|
88
|
+
":",
|
89
|
+
"{",
|
90
|
+
"=>",
|
91
|
+
"*=",
|
92
|
+
"<<=",
|
93
|
+
">>=",
|
94
|
+
">>>=",
|
95
|
+
"^=",
|
96
|
+
"|=",
|
97
|
+
"&=",
|
98
|
+
"??=",
|
99
|
+
"||=",
|
100
|
+
"&&=",
|
101
|
+
"**=",
|
102
|
+
"+=",
|
103
|
+
"-=",
|
104
|
+
"/=",
|
105
|
+
"%=",
|
106
|
+
"/",
|
107
|
+
"do",
|
108
|
+
"break",
|
109
|
+
"continue",
|
110
|
+
"debugger",
|
111
|
+
"case",
|
112
|
+
"throw"
|
113
|
+
];
|
114
|
+
|
46
115
|
|
47
116
|
//------------------------------------------------------------------------------
|
48
117
|
// Rule Definition
|
@@ -59,6 +128,8 @@ module.exports = {
|
|
59
128
|
url: "https://eslint.org/docs/rules/prefer-regex-literals"
|
60
129
|
},
|
61
130
|
|
131
|
+
hasSuggestions: true,
|
132
|
+
|
62
133
|
schema: [
|
63
134
|
{
|
64
135
|
type: "object",
|
@@ -74,6 +145,7 @@ module.exports = {
|
|
74
145
|
|
75
146
|
messages: {
|
76
147
|
unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor.",
|
148
|
+
replaceWithLiteral: "Replace with an equivalent regular expression literal.",
|
77
149
|
unexpectedRedundantRegExp: "Regular expression literal is unnecessarily wrapped within a 'RegExp' constructor.",
|
78
150
|
unexpectedRedundantRegExpWithFlags: "Use regular expression literal with flags instead of the 'RegExp' constructor."
|
79
151
|
}
|
@@ -81,6 +153,7 @@ module.exports = {
|
|
81
153
|
|
82
154
|
create(context) {
|
83
155
|
const [{ disallowRedundantWrapping = false } = {}] = context.options;
|
156
|
+
const sourceCode = context.getSourceCode();
|
84
157
|
|
85
158
|
/**
|
86
159
|
* Determines whether the given identifier node is a reference to a global variable.
|
@@ -107,6 +180,27 @@ module.exports = {
|
|
107
180
|
isStaticTemplateLiteral(node.quasi);
|
108
181
|
}
|
109
182
|
|
183
|
+
/**
|
184
|
+
* Gets the value of a string
|
185
|
+
* @param {ASTNode} node The node to get the string of.
|
186
|
+
* @returns {string|null} The value of the node.
|
187
|
+
*/
|
188
|
+
function getStringValue(node) {
|
189
|
+
if (isStringLiteral(node)) {
|
190
|
+
return node.value;
|
191
|
+
}
|
192
|
+
|
193
|
+
if (isStaticTemplateLiteral(node)) {
|
194
|
+
return node.quasis[0].value.cooked;
|
195
|
+
}
|
196
|
+
|
197
|
+
if (isStringRawTaggedStaticTemplateLiteral(node)) {
|
198
|
+
return node.quasi.quasis[0].value.raw;
|
199
|
+
}
|
200
|
+
|
201
|
+
return null;
|
202
|
+
}
|
203
|
+
|
110
204
|
/**
|
111
205
|
* Determines whether the given node is considered to be a static string by the logic of this rule.
|
112
206
|
* @param {ASTNode} node Node to check.
|
@@ -152,6 +246,53 @@ module.exports = {
|
|
152
246
|
return false;
|
153
247
|
}
|
154
248
|
|
249
|
+
/**
|
250
|
+
* Returns a ecmaVersion compatible for regexpp.
|
251
|
+
* @param {any} ecmaVersion The ecmaVersion to convert.
|
252
|
+
* @returns {import("regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
|
253
|
+
*/
|
254
|
+
function getRegexppEcmaVersion(ecmaVersion) {
|
255
|
+
if (typeof ecmaVersion !== "number" || ecmaVersion <= 5) {
|
256
|
+
return 5;
|
257
|
+
}
|
258
|
+
return Math.min(ecmaVersion + 2009, REGEXPP_LATEST_ECMA_VERSION);
|
259
|
+
}
|
260
|
+
|
261
|
+
/**
|
262
|
+
* Makes a character escaped or else returns null.
|
263
|
+
* @param {string} character The character to escape.
|
264
|
+
* @returns {string} The resulting escaped character.
|
265
|
+
*/
|
266
|
+
function resolveEscapes(character) {
|
267
|
+
switch (character) {
|
268
|
+
case "\n":
|
269
|
+
case "\\\n":
|
270
|
+
return "\\n";
|
271
|
+
|
272
|
+
case "\r":
|
273
|
+
case "\\\r":
|
274
|
+
return "\\r";
|
275
|
+
|
276
|
+
case "\t":
|
277
|
+
case "\\\t":
|
278
|
+
return "\\t";
|
279
|
+
|
280
|
+
case "\v":
|
281
|
+
case "\\\v":
|
282
|
+
return "\\v";
|
283
|
+
|
284
|
+
case "\f":
|
285
|
+
case "\\\f":
|
286
|
+
return "\\f";
|
287
|
+
|
288
|
+
case "/":
|
289
|
+
return "\\/";
|
290
|
+
|
291
|
+
default:
|
292
|
+
return null;
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
155
296
|
return {
|
156
297
|
Program() {
|
157
298
|
const scope = context.getScope();
|
@@ -171,7 +312,82 @@ module.exports = {
|
|
171
312
|
context.report({ node, messageId: "unexpectedRedundantRegExp" });
|
172
313
|
}
|
173
314
|
} else if (hasOnlyStaticStringArguments(node)) {
|
174
|
-
|
315
|
+
let regexContent = getStringValue(node.arguments[0]);
|
316
|
+
let noFix = false;
|
317
|
+
let flags;
|
318
|
+
|
319
|
+
if (node.arguments[1]) {
|
320
|
+
flags = getStringValue(node.arguments[1]);
|
321
|
+
}
|
322
|
+
|
323
|
+
const regexppEcmaVersion = getRegexppEcmaVersion(context.parserOptions.ecmaVersion);
|
324
|
+
const RegExpValidatorInstance = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
|
325
|
+
|
326
|
+
try {
|
327
|
+
RegExpValidatorInstance.validatePattern(regexContent, 0, regexContent.length, flags ? flags.includes("u") : false);
|
328
|
+
if (flags) {
|
329
|
+
RegExpValidatorInstance.validateFlags(flags);
|
330
|
+
}
|
331
|
+
} catch {
|
332
|
+
noFix = true;
|
333
|
+
}
|
334
|
+
|
335
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
336
|
+
|
337
|
+
if (tokenBefore && !validPrecedingTokens.includes(tokenBefore.value)) {
|
338
|
+
noFix = true;
|
339
|
+
}
|
340
|
+
|
341
|
+
if (!/^[-a-zA-Z0-9\\[\](){} \t\r\n\v\f!@#$%^&*+^_=/~`.><?,'"|:;]*$/u.test(regexContent)) {
|
342
|
+
noFix = true;
|
343
|
+
}
|
344
|
+
|
345
|
+
if (sourceCode.getCommentsInside(node).length > 0) {
|
346
|
+
noFix = true;
|
347
|
+
}
|
348
|
+
|
349
|
+
if (regexContent && !noFix) {
|
350
|
+
let charIncrease = 0;
|
351
|
+
|
352
|
+
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length, flags ? flags.includes("u") : false);
|
353
|
+
|
354
|
+
visitRegExpAST(ast, {
|
355
|
+
onCharacterEnter(characterNode) {
|
356
|
+
const escaped = resolveEscapes(characterNode.raw);
|
357
|
+
|
358
|
+
if (escaped) {
|
359
|
+
regexContent =
|
360
|
+
regexContent.slice(0, characterNode.start + charIncrease) +
|
361
|
+
escaped +
|
362
|
+
regexContent.slice(characterNode.end + charIncrease);
|
363
|
+
|
364
|
+
if (characterNode.raw.length === 1) {
|
365
|
+
charIncrease += 1;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
}
|
369
|
+
});
|
370
|
+
}
|
371
|
+
|
372
|
+
const newRegExpValue = `/${regexContent || "(?:)"}/${flags || ""}`;
|
373
|
+
|
374
|
+
context.report({
|
375
|
+
node,
|
376
|
+
messageId: "unexpectedRegExp",
|
377
|
+
suggest: noFix ? [] : [{
|
378
|
+
messageId: "replaceWithLiteral",
|
379
|
+
fix(fixer) {
|
380
|
+
const tokenAfter = sourceCode.getTokenAfter(node);
|
381
|
+
|
382
|
+
return fixer.replaceText(
|
383
|
+
node,
|
384
|
+
(tokenBefore && !canTokensBeAdjacent(tokenBefore, newRegExpValue) && tokenBefore.range[1] === node.range[0] ? " " : "") +
|
385
|
+
newRegExpValue +
|
386
|
+
(tokenAfter && !canTokensBeAdjacent(newRegExpValue, tokenAfter) && node.range[1] === tokenAfter.range[0] ? " " : "")
|
387
|
+
);
|
388
|
+
}
|
389
|
+
}]
|
390
|
+
});
|
175
391
|
}
|
176
392
|
}
|
177
393
|
}
|
@@ -188,7 +188,7 @@ module.exports = {
|
|
188
188
|
return sourceCode.getText(currentNode);
|
189
189
|
}
|
190
190
|
|
191
|
-
if (isConcatenation(currentNode) && hasStringLiteral(currentNode)
|
191
|
+
if (isConcatenation(currentNode) && hasStringLiteral(currentNode)) {
|
192
192
|
const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
|
193
193
|
const textBeforePlus = getTextBetween(currentNode.left, plusSign);
|
194
194
|
const textAfterPlus = getTextBetween(plusSign, currentNode.right);
|
package/lib/rules/quotes.js
CHANGED
@@ -223,9 +223,20 @@ module.exports = {
|
|
223
223
|
// ModuleSpecifier.
|
224
224
|
case "ImportDeclaration":
|
225
225
|
case "ExportNamedDeclaration":
|
226
|
-
case "ExportAllDeclaration":
|
227
226
|
return parent.source === node;
|
228
227
|
|
228
|
+
// ModuleExportName or ModuleSpecifier.
|
229
|
+
case "ExportAllDeclaration":
|
230
|
+
return parent.exported === node || parent.source === node;
|
231
|
+
|
232
|
+
// ModuleExportName.
|
233
|
+
case "ImportSpecifier":
|
234
|
+
return parent.imported === node;
|
235
|
+
|
236
|
+
// ModuleExportName.
|
237
|
+
case "ExportSpecifier":
|
238
|
+
return parent.local === node || parent.exported === node;
|
239
|
+
|
229
240
|
// Others don't allow.
|
230
241
|
default:
|
231
242
|
return false;
|
@@ -769,6 +769,25 @@ function getSwitchCaseColonToken(node, sourceCode) {
|
|
769
769
|
return sourceCode.getFirstToken(node, 1);
|
770
770
|
}
|
771
771
|
|
772
|
+
/**
|
773
|
+
* Gets ESM module export name represented by the given node.
|
774
|
+
* @param {ASTNode} node `Identifier` or string `Literal` node in a position
|
775
|
+
* that represents a module export name:
|
776
|
+
* - `ImportSpecifier#imported`
|
777
|
+
* - `ExportSpecifier#local` (if it is a re-export from another module)
|
778
|
+
* - `ExportSpecifier#exported`
|
779
|
+
* - `ExportAllDeclaration#exported`
|
780
|
+
* @returns {string} The module export name.
|
781
|
+
*/
|
782
|
+
function getModuleExportName(node) {
|
783
|
+
if (node.type === "Identifier") {
|
784
|
+
return node.name;
|
785
|
+
}
|
786
|
+
|
787
|
+
// string literal
|
788
|
+
return node.value;
|
789
|
+
}
|
790
|
+
|
772
791
|
//------------------------------------------------------------------------------
|
773
792
|
// Public Interface
|
774
793
|
//------------------------------------------------------------------------------
|
@@ -1898,5 +1917,6 @@ module.exports = {
|
|
1898
1917
|
equalLiteralValue,
|
1899
1918
|
isSameReference,
|
1900
1919
|
isLogicalAssignmentOperator,
|
1901
|
-
getSwitchCaseColonToken
|
1920
|
+
getSwitchCaseColonToken,
|
1921
|
+
getModuleExportName
|
1902
1922
|
};
|
package/lib/shared/types.js
CHANGED
@@ -105,6 +105,21 @@ module.exports = {};
|
|
105
105
|
* @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions.
|
106
106
|
*/
|
107
107
|
|
108
|
+
/**
|
109
|
+
* @typedef {Object} SuppressedLintMessage
|
110
|
+
* @property {number|undefined} column The 1-based column number.
|
111
|
+
* @property {number} [endColumn] The 1-based column number of the end location.
|
112
|
+
* @property {number} [endLine] The 1-based line number of the end location.
|
113
|
+
* @property {boolean} fatal If `true` then this is a fatal error.
|
114
|
+
* @property {{range:[number,number], text:string}} [fix] Information for autofix.
|
115
|
+
* @property {number|undefined} line The 1-based line number.
|
116
|
+
* @property {string} message The error message.
|
117
|
+
* @property {string|null} ruleId The ID of the rule which makes this message.
|
118
|
+
* @property {0|1|2} severity The severity of this message.
|
119
|
+
* @property {Array<{kind: string, justification: string}>} suppressions The suppression info.
|
120
|
+
* @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions.
|
121
|
+
*/
|
122
|
+
|
108
123
|
/**
|
109
124
|
* @typedef {Object} SuggestionResult
|
110
125
|
* @property {string} desc A short description.
|
@@ -6,7 +6,7 @@ module.exports = function(it) {
|
|
6
6
|
return `
|
7
7
|
ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:
|
8
8
|
|
9
|
-
|
9
|
+
npm init @eslint/config
|
10
10
|
|
11
11
|
ESLint looked for configuration files in ${directoryPath} and its ancestors. If it found none, it then looked in your home directory.
|
12
12
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.8.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -54,12 +54,11 @@
|
|
54
54
|
"cross-spawn": "^7.0.2",
|
55
55
|
"debug": "^4.3.2",
|
56
56
|
"doctrine": "^3.0.0",
|
57
|
-
"enquirer": "^2.3.5",
|
58
57
|
"escape-string-regexp": "^4.0.0",
|
59
58
|
"eslint-scope": "^7.1.0",
|
60
59
|
"eslint-utils": "^3.0.0",
|
61
|
-
"eslint-visitor-keys": "^3.
|
62
|
-
"espree": "^9.
|
60
|
+
"eslint-visitor-keys": "^3.2.0",
|
61
|
+
"espree": "^9.3.0",
|
63
62
|
"esquery": "^1.4.0",
|
64
63
|
"esutils": "^2.0.2",
|
65
64
|
"fast-deep-equal": "^3.1.3",
|
@@ -67,7 +66,7 @@
|
|
67
66
|
"functional-red-black-tree": "^1.0.1",
|
68
67
|
"glob-parent": "^6.0.1",
|
69
68
|
"globals": "^13.6.0",
|
70
|
-
"ignore": "^
|
69
|
+
"ignore": "^5.2.0",
|
71
70
|
"import-fresh": "^3.0.0",
|
72
71
|
"imurmurhash": "^0.1.4",
|
73
72
|
"is-glob": "^4.0.0",
|
@@ -78,9 +77,7 @@
|
|
78
77
|
"minimatch": "^3.0.4",
|
79
78
|
"natural-compare": "^1.4.0",
|
80
79
|
"optionator": "^0.9.1",
|
81
|
-
"progress": "^2.0.0",
|
82
80
|
"regexpp": "^3.2.0",
|
83
|
-
"semver": "^7.2.1",
|
84
81
|
"strip-ansi": "^6.0.1",
|
85
82
|
"strip-json-comments": "^3.1.0",
|
86
83
|
"text-table": "^0.2.0",
|
@@ -118,16 +115,19 @@
|
|
118
115
|
"load-perf": "^0.2.0",
|
119
116
|
"markdownlint": "^0.24.0",
|
120
117
|
"markdownlint-cli": "^0.30.0",
|
118
|
+
"marked": "^4.0.8",
|
121
119
|
"memfs": "^3.0.1",
|
122
120
|
"mocha": "^8.3.2",
|
123
121
|
"mocha-junit-reporter": "^2.0.0",
|
124
122
|
"node-polyfill-webpack-plugin": "^1.0.3",
|
125
123
|
"npm-license": "^0.3.3",
|
126
124
|
"nyc": "^15.0.1",
|
125
|
+
"progress": "^2.0.3",
|
127
126
|
"proxyquire": "^2.0.1",
|
128
127
|
"puppeteer": "^9.1.1",
|
129
128
|
"recast": "^0.20.4",
|
130
129
|
"regenerator-runtime": "^0.13.2",
|
130
|
+
"semver": "^7.3.5",
|
131
131
|
"shelljs": "^0.8.2",
|
132
132
|
"sinon": "^11.0.0",
|
133
133
|
"temp": "^0.9.0",
|