eslint 8.49.0 → 8.50.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 +1 -1
- package/lib/config/flat-config-schema.js +11 -1
- package/lib/linter/linter.js +172 -57
- package/lib/rule-tester/flat-rule-tester.js +50 -5
- package/lib/rule-tester/rule-tester.js +78 -20
- package/lib/rules/array-callback-return.js +139 -14
- package/lib/rules/index.js +1 -0
- package/lib/rules/no-misleading-character-class.js +65 -15
- package/lib/rules/no-new-object.js +7 -0
- package/lib/rules/no-object-constructor.js +118 -0
- package/lib/source-code/source-code.js +350 -3
- package/package.json +2 -2
@@ -12,7 +12,15 @@ const
|
|
12
12
|
{ isCommentToken } = require("@eslint-community/eslint-utils"),
|
13
13
|
TokenStore = require("./token-store"),
|
14
14
|
astUtils = require("../shared/ast-utils"),
|
15
|
-
Traverser = require("../shared/traverser")
|
15
|
+
Traverser = require("../shared/traverser"),
|
16
|
+
globals = require("../../conf/globals"),
|
17
|
+
{
|
18
|
+
directivesPattern
|
19
|
+
} = require("../shared/directives"),
|
20
|
+
|
21
|
+
/* eslint-disable-next-line n/no-restricted-require -- Too messy to figure out right now. */
|
22
|
+
ConfigCommentParser = require("../linter/config-comment-parser"),
|
23
|
+
eslintScope = require("eslint-scope");
|
16
24
|
|
17
25
|
//------------------------------------------------------------------------------
|
18
26
|
// Type Definitions
|
@@ -24,6 +32,8 @@ const
|
|
24
32
|
// Private
|
25
33
|
//------------------------------------------------------------------------------
|
26
34
|
|
35
|
+
const commentParser = new ConfigCommentParser();
|
36
|
+
|
27
37
|
/**
|
28
38
|
* Validates that the given AST has the required information.
|
29
39
|
* @param {ASTNode} ast The Program node of the AST to check.
|
@@ -49,6 +59,29 @@ function validate(ast) {
|
|
49
59
|
}
|
50
60
|
}
|
51
61
|
|
62
|
+
/**
|
63
|
+
* Retrieves globals for the given ecmaVersion.
|
64
|
+
* @param {number} ecmaVersion The version to retrieve globals for.
|
65
|
+
* @returns {Object} The globals for the given ecmaVersion.
|
66
|
+
*/
|
67
|
+
function getGlobalsForEcmaVersion(ecmaVersion) {
|
68
|
+
|
69
|
+
switch (ecmaVersion) {
|
70
|
+
case 3:
|
71
|
+
return globals.es3;
|
72
|
+
|
73
|
+
case 5:
|
74
|
+
return globals.es5;
|
75
|
+
|
76
|
+
default:
|
77
|
+
if (ecmaVersion < 2015) {
|
78
|
+
return globals[`es${ecmaVersion + 2009}`];
|
79
|
+
}
|
80
|
+
|
81
|
+
return globals[`es${ecmaVersion}`];
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
52
85
|
/**
|
53
86
|
* Check to see if its a ES6 export declaration.
|
54
87
|
* @param {ASTNode} astNode An AST node.
|
@@ -83,6 +116,36 @@ function sortedMerge(tokens, comments) {
|
|
83
116
|
return result;
|
84
117
|
}
|
85
118
|
|
119
|
+
/**
|
120
|
+
* Normalizes a value for a global in a config
|
121
|
+
* @param {(boolean|string|null)} configuredValue The value given for a global in configuration or in
|
122
|
+
* a global directive comment
|
123
|
+
* @returns {("readable"|"writeable"|"off")} The value normalized as a string
|
124
|
+
* @throws Error if global value is invalid
|
125
|
+
*/
|
126
|
+
function normalizeConfigGlobal(configuredValue) {
|
127
|
+
switch (configuredValue) {
|
128
|
+
case "off":
|
129
|
+
return "off";
|
130
|
+
|
131
|
+
case true:
|
132
|
+
case "true":
|
133
|
+
case "writeable":
|
134
|
+
case "writable":
|
135
|
+
return "writable";
|
136
|
+
|
137
|
+
case null:
|
138
|
+
case false:
|
139
|
+
case "false":
|
140
|
+
case "readable":
|
141
|
+
case "readonly":
|
142
|
+
return "readonly";
|
143
|
+
|
144
|
+
default:
|
145
|
+
throw new Error(`'${configuredValue}' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')`);
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
86
149
|
/**
|
87
150
|
* Determines if two nodes or tokens overlap.
|
88
151
|
* @param {ASTNode|Token} first The first node or token to check.
|
@@ -145,6 +208,116 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
|
|
145
208
|
return false;
|
146
209
|
}
|
147
210
|
|
211
|
+
//-----------------------------------------------------------------------------
|
212
|
+
// Directive Comments
|
213
|
+
//-----------------------------------------------------------------------------
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Extract the directive and the justification from a given directive comment and trim them.
|
217
|
+
* @param {string} value The comment text to extract.
|
218
|
+
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
|
219
|
+
*/
|
220
|
+
function extractDirectiveComment(value) {
|
221
|
+
const match = /\s-{2,}\s/u.exec(value);
|
222
|
+
|
223
|
+
if (!match) {
|
224
|
+
return { directivePart: value.trim(), justificationPart: "" };
|
225
|
+
}
|
226
|
+
|
227
|
+
const directive = value.slice(0, match.index).trim();
|
228
|
+
const justification = value.slice(match.index + match[0].length).trim();
|
229
|
+
|
230
|
+
return { directivePart: directive, justificationPart: justification };
|
231
|
+
}
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Ensures that variables representing built-in properties of the Global Object,
|
235
|
+
* and any globals declared by special block comments, are present in the global
|
236
|
+
* scope.
|
237
|
+
* @param {Scope} globalScope The global scope.
|
238
|
+
* @param {Object|undefined} configGlobals The globals declared in configuration
|
239
|
+
* @param {Object|undefined} inlineGlobals The globals declared in the source code
|
240
|
+
* @returns {void}
|
241
|
+
*/
|
242
|
+
function addDeclaredGlobals(globalScope, configGlobals = {}, inlineGlobals = {}) {
|
243
|
+
|
244
|
+
// Define configured global variables.
|
245
|
+
for (const id of new Set([...Object.keys(configGlobals), ...Object.keys(inlineGlobals)])) {
|
246
|
+
|
247
|
+
/*
|
248
|
+
* `normalizeConfigGlobal` will throw an error if a configured global value is invalid. However, these errors would
|
249
|
+
* typically be caught when validating a config anyway (validity for inline global comments is checked separately).
|
250
|
+
*/
|
251
|
+
const configValue = configGlobals[id] === void 0 ? void 0 : normalizeConfigGlobal(configGlobals[id]);
|
252
|
+
const commentValue = inlineGlobals[id] && inlineGlobals[id].value;
|
253
|
+
const value = commentValue || configValue;
|
254
|
+
const sourceComments = inlineGlobals[id] && inlineGlobals[id].comments;
|
255
|
+
|
256
|
+
if (value === "off") {
|
257
|
+
continue;
|
258
|
+
}
|
259
|
+
|
260
|
+
let variable = globalScope.set.get(id);
|
261
|
+
|
262
|
+
if (!variable) {
|
263
|
+
variable = new eslintScope.Variable(id, globalScope);
|
264
|
+
|
265
|
+
globalScope.variables.push(variable);
|
266
|
+
globalScope.set.set(id, variable);
|
267
|
+
}
|
268
|
+
|
269
|
+
variable.eslintImplicitGlobalSetting = configValue;
|
270
|
+
variable.eslintExplicitGlobal = sourceComments !== void 0;
|
271
|
+
variable.eslintExplicitGlobalComments = sourceComments;
|
272
|
+
variable.writeable = (value === "writable");
|
273
|
+
}
|
274
|
+
|
275
|
+
/*
|
276
|
+
* "through" contains all references which definitions cannot be found.
|
277
|
+
* Since we augment the global scope using configuration, we need to update
|
278
|
+
* references and remove the ones that were added by configuration.
|
279
|
+
*/
|
280
|
+
globalScope.through = globalScope.through.filter(reference => {
|
281
|
+
const name = reference.identifier.name;
|
282
|
+
const variable = globalScope.set.get(name);
|
283
|
+
|
284
|
+
if (variable) {
|
285
|
+
|
286
|
+
/*
|
287
|
+
* Links the variable and the reference.
|
288
|
+
* And this reference is removed from `Scope#through`.
|
289
|
+
*/
|
290
|
+
reference.resolved = variable;
|
291
|
+
variable.references.push(reference);
|
292
|
+
|
293
|
+
return false;
|
294
|
+
}
|
295
|
+
|
296
|
+
return true;
|
297
|
+
});
|
298
|
+
}
|
299
|
+
|
300
|
+
/**
|
301
|
+
* Sets the given variable names as exported so they won't be triggered by
|
302
|
+
* the `no-unused-vars` rule.
|
303
|
+
* @param {eslint.Scope} globalScope The global scope to define exports in.
|
304
|
+
* @param {Record<string,string>} variables An object whose keys are the variable
|
305
|
+
* names to export.
|
306
|
+
* @returns {void}
|
307
|
+
*/
|
308
|
+
function markExportedVariables(globalScope, variables) {
|
309
|
+
|
310
|
+
Object.keys(variables).forEach(name => {
|
311
|
+
const variable = globalScope.set.get(name);
|
312
|
+
|
313
|
+
if (variable) {
|
314
|
+
variable.eslintUsed = true;
|
315
|
+
variable.eslintExported = true;
|
316
|
+
}
|
317
|
+
});
|
318
|
+
|
319
|
+
}
|
320
|
+
|
148
321
|
//------------------------------------------------------------------------------
|
149
322
|
// Public Interface
|
150
323
|
//------------------------------------------------------------------------------
|
@@ -187,7 +360,9 @@ class SourceCode extends TokenStore {
|
|
187
360
|
* General purpose caching for the class.
|
188
361
|
*/
|
189
362
|
this[caches] = new Map([
|
190
|
-
["scopes", new WeakMap()]
|
363
|
+
["scopes", new WeakMap()],
|
364
|
+
["vars", new Map()],
|
365
|
+
["configNodes", void 0]
|
191
366
|
]);
|
192
367
|
|
193
368
|
/**
|
@@ -266,7 +441,7 @@ class SourceCode extends TokenStore {
|
|
266
441
|
// Cache for comments found using getComments().
|
267
442
|
this._commentCache = new WeakMap();
|
268
443
|
|
269
|
-
// don't allow modification of this object
|
444
|
+
// don't allow further modification of this object
|
270
445
|
Object.freeze(this);
|
271
446
|
Object.freeze(this.lines);
|
272
447
|
}
|
@@ -724,6 +899,178 @@ class SourceCode extends TokenStore {
|
|
724
899
|
}
|
725
900
|
|
726
901
|
|
902
|
+
/**
|
903
|
+
* Returns an array of all inline configuration nodes found in the
|
904
|
+
* source code.
|
905
|
+
* @returns {Array<Token>} An array of all inline configuration nodes.
|
906
|
+
*/
|
907
|
+
getInlineConfigNodes() {
|
908
|
+
|
909
|
+
// check the cache first
|
910
|
+
let configNodes = this[caches].get("configNodes");
|
911
|
+
|
912
|
+
if (configNodes) {
|
913
|
+
return configNodes;
|
914
|
+
}
|
915
|
+
|
916
|
+
// calculate fresh config nodes
|
917
|
+
configNodes = this.ast.comments.filter(comment => {
|
918
|
+
|
919
|
+
// shebang comments are never directives
|
920
|
+
if (comment.type === "Shebang") {
|
921
|
+
return false;
|
922
|
+
}
|
923
|
+
|
924
|
+
const { directivePart } = extractDirectiveComment(comment.value);
|
925
|
+
|
926
|
+
const directiveMatch = directivesPattern.exec(directivePart);
|
927
|
+
|
928
|
+
if (!directiveMatch) {
|
929
|
+
return false;
|
930
|
+
}
|
931
|
+
|
932
|
+
// only certain comment types are supported as line comments
|
933
|
+
return comment.type !== "Line" || !!/^eslint-disable-(next-)?line$/u.test(directiveMatch[1]);
|
934
|
+
});
|
935
|
+
|
936
|
+
this[caches].set("configNodes", configNodes);
|
937
|
+
|
938
|
+
return configNodes;
|
939
|
+
}
|
940
|
+
|
941
|
+
/**
|
942
|
+
* Applies language options sent in from the core.
|
943
|
+
* @param {Object} languageOptions The language options for this run.
|
944
|
+
* @returns {void}
|
945
|
+
*/
|
946
|
+
applyLanguageOptions(languageOptions) {
|
947
|
+
|
948
|
+
/*
|
949
|
+
* Add configured globals and language globals
|
950
|
+
*
|
951
|
+
* Using Object.assign instead of object spread for performance reasons
|
952
|
+
* https://github.com/eslint/eslint/issues/16302
|
953
|
+
*/
|
954
|
+
const configGlobals = Object.assign(
|
955
|
+
{},
|
956
|
+
getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
|
957
|
+
languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
|
958
|
+
languageOptions.globals
|
959
|
+
);
|
960
|
+
const varsCache = this[caches].get("vars");
|
961
|
+
|
962
|
+
varsCache.set("configGlobals", configGlobals);
|
963
|
+
}
|
964
|
+
|
965
|
+
/**
|
966
|
+
* Applies configuration found inside of the source code. This method is only
|
967
|
+
* called when ESLint is running with inline configuration allowed.
|
968
|
+
* @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,node:ASTNode}}} Information
|
969
|
+
* that ESLint needs to further process the inline configuration.
|
970
|
+
*/
|
971
|
+
applyInlineConfig() {
|
972
|
+
|
973
|
+
const problems = [];
|
974
|
+
const configs = [];
|
975
|
+
const exportedVariables = {};
|
976
|
+
const inlineGlobals = Object.create(null);
|
977
|
+
|
978
|
+
this.getInlineConfigNodes().forEach(comment => {
|
979
|
+
|
980
|
+
const { directivePart } = extractDirectiveComment(comment.value);
|
981
|
+
const match = directivesPattern.exec(directivePart);
|
982
|
+
const directiveText = match[1];
|
983
|
+
const directiveValue = directivePart.slice(match.index + directiveText.length);
|
984
|
+
|
985
|
+
switch (directiveText) {
|
986
|
+
case "exported":
|
987
|
+
Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
|
988
|
+
break;
|
989
|
+
|
990
|
+
case "globals":
|
991
|
+
case "global":
|
992
|
+
for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
|
993
|
+
let normalizedValue;
|
994
|
+
|
995
|
+
try {
|
996
|
+
normalizedValue = normalizeConfigGlobal(value);
|
997
|
+
} catch (err) {
|
998
|
+
problems.push({
|
999
|
+
ruleId: null,
|
1000
|
+
loc: comment.loc,
|
1001
|
+
message: err.message
|
1002
|
+
});
|
1003
|
+
continue;
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
if (inlineGlobals[id]) {
|
1007
|
+
inlineGlobals[id].comments.push(comment);
|
1008
|
+
inlineGlobals[id].value = normalizedValue;
|
1009
|
+
} else {
|
1010
|
+
inlineGlobals[id] = {
|
1011
|
+
comments: [comment],
|
1012
|
+
value: normalizedValue
|
1013
|
+
};
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
break;
|
1017
|
+
|
1018
|
+
case "eslint": {
|
1019
|
+
const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
|
1020
|
+
|
1021
|
+
if (parseResult.success) {
|
1022
|
+
configs.push({
|
1023
|
+
config: {
|
1024
|
+
rules: parseResult.config
|
1025
|
+
},
|
1026
|
+
node: comment
|
1027
|
+
});
|
1028
|
+
} else {
|
1029
|
+
problems.push(parseResult.error);
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
break;
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
// no default
|
1036
|
+
}
|
1037
|
+
});
|
1038
|
+
|
1039
|
+
// save all the new variables for later
|
1040
|
+
const varsCache = this[caches].get("vars");
|
1041
|
+
|
1042
|
+
varsCache.set("inlineGlobals", inlineGlobals);
|
1043
|
+
varsCache.set("exportedVariables", exportedVariables);
|
1044
|
+
|
1045
|
+
return {
|
1046
|
+
configs,
|
1047
|
+
problems
|
1048
|
+
};
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
/**
|
1052
|
+
* Called by ESLint core to indicate that it has finished providing
|
1053
|
+
* information. We now add in all the missing variables and ensure that
|
1054
|
+
* state-changing methods cannot be called by rules.
|
1055
|
+
* @returns {void}
|
1056
|
+
*/
|
1057
|
+
finalize() {
|
1058
|
+
|
1059
|
+
// Step 1: ensure that all of the necessary variables are up to date
|
1060
|
+
const varsCache = this[caches].get("vars");
|
1061
|
+
const globalScope = this.scopeManager.scopes[0];
|
1062
|
+
const configGlobals = varsCache.get("configGlobals");
|
1063
|
+
const inlineGlobals = varsCache.get("inlineGlobals");
|
1064
|
+
const exportedVariables = varsCache.get("exportedVariables");
|
1065
|
+
|
1066
|
+
addDeclaredGlobals(globalScope, configGlobals, inlineGlobals);
|
1067
|
+
|
1068
|
+
if (exportedVariables) {
|
1069
|
+
markExportedVariables(globalScope, exportedVariables);
|
1070
|
+
}
|
1071
|
+
|
1072
|
+
}
|
1073
|
+
|
727
1074
|
}
|
728
1075
|
|
729
1076
|
module.exports = SourceCode;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.50.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -63,7 +63,7 @@
|
|
63
63
|
"@eslint-community/eslint-utils": "^4.2.0",
|
64
64
|
"@eslint-community/regexpp": "^4.6.1",
|
65
65
|
"@eslint/eslintrc": "^2.1.2",
|
66
|
-
"@eslint/js": "8.
|
66
|
+
"@eslint/js": "8.50.0",
|
67
67
|
"@humanwhocodes/config-array": "^0.11.11",
|
68
68
|
"@humanwhocodes/module-importer": "^1.0.1",
|
69
69
|
"@nodelib/fs.walk": "^1.2.8",
|