eslint 8.1.0 → 8.2.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 CHANGED
@@ -298,7 +298,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
298
298
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
299
299
  <p><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>
300
300
  <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>
301
- <p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" height="32"></a> <a href="https://mobilen.nu"><img src="https://images.opencollective.com/mobilen/e19860d/logo.png" alt="Mobilen" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" 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.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" 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>
301
+ <p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" 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.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" 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>
302
302
  <!--sponsorsend-->
303
303
 
304
304
  ## <a name="technology-sponsors"></a>Technology Sponsors
@@ -427,19 +427,7 @@ module.exports = {
427
427
  * @returns {void}
428
428
  */
429
429
  function report(property, side, whitespace, expected, mode) {
430
- const diff = whitespace.length - expected,
431
- nextColon = getNextColon(property.key),
432
- tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
433
- tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
434
- isKeySide = side === "key",
435
- isExtra = diff > 0,
436
- diffAbs = Math.abs(diff),
437
- spaces = Array(diffAbs + 1).join(" ");
438
-
439
- const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
440
- const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
441
- const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
442
- const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
430
+ const diff = whitespace.length - expected;
443
431
 
444
432
  if ((
445
433
  diff && mode === "strict" ||
@@ -447,6 +435,19 @@ module.exports = {
447
435
  diff > 0 && !expected && mode === "minimum") &&
448
436
  !(expected && containsLineTerminator(whitespace))
449
437
  ) {
438
+ const nextColon = getNextColon(property.key),
439
+ tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
440
+ tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
441
+ isKeySide = side === "key",
442
+ isExtra = diff > 0,
443
+ diffAbs = Math.abs(diff),
444
+ spaces = Array(diffAbs + 1).join(" ");
445
+
446
+ const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
447
+ const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
448
+ const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
449
+ const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
450
+
450
451
  let fix;
451
452
 
452
453
  if (isExtra) {
@@ -34,52 +34,91 @@ function parseOptions(options) {
34
34
  }
35
35
 
36
36
  /**
37
- * Checks whether or not a given variable is a function declaration.
38
- * @param {eslint-scope.Variable} variable A variable to check.
39
- * @returns {boolean} `true` if the variable is a function declaration.
37
+ * Checks whether or not a given location is inside of the range of a given node.
38
+ * @param {ASTNode} node An node to check.
39
+ * @param {number} location A location to check.
40
+ * @returns {boolean} `true` if the location is inside of the range of the node.
40
41
  */
41
- function isFunction(variable) {
42
- return variable.defs[0].type === "FunctionName";
42
+ function isInRange(node, location) {
43
+ return node && node.range[0] <= location && location <= node.range[1];
43
44
  }
44
45
 
45
46
  /**
46
- * Checks whether or not a given variable is a class declaration in an upper function scope.
47
- * @param {eslint-scope.Variable} variable A variable to check.
48
- * @param {eslint-scope.Reference} reference A reference to check.
49
- * @returns {boolean} `true` if the variable is a class declaration.
47
+ * Checks whether or not a given location is inside of the range of a class static initializer.
48
+ * @param {ASTNode} node `ClassBody` node to check static initializers.
49
+ * @param {number} location A location to check.
50
+ * @returns {boolean} `true` if the location is inside of a class static initializer.
50
51
  */
51
- function isOuterClass(variable, reference) {
52
- return (
53
- variable.defs[0].type === "ClassName" &&
54
- variable.scope.variableScope !== reference.from.variableScope
55
- );
52
+ function isInClassStaticInitializerRange(node, location) {
53
+ return node.body.some(classMember => (
54
+ classMember.type === "PropertyDefinition" &&
55
+ classMember.static &&
56
+ classMember.value &&
57
+ isInRange(classMember.value, location)
58
+ ));
56
59
  }
57
60
 
58
61
  /**
59
- * Checks whether or not a given variable is a variable declaration in an upper function scope.
60
- * @param {eslint-scope.Variable} variable A variable to check.
61
- * @param {eslint-scope.Reference} reference A reference to check.
62
- * @returns {boolean} `true` if the variable is a variable declaration.
62
+ * Checks whether a given scope is the scope of a static class field initializer.
63
+ * @param {eslint-scope.Scope} scope A scope to check.
64
+ * @returns {boolean} `true` if the scope is a class static initializer scope.
63
65
  */
64
- function isOuterVariable(variable, reference) {
65
- return (
66
- variable.defs[0].type === "Variable" &&
67
- variable.scope.variableScope !== reference.from.variableScope
68
- );
66
+ function isClassStaticInitializerScope(scope) {
67
+ if (scope.type === "class-field-initializer") {
68
+
69
+ // `scope.block` is PropertyDefinition#value node
70
+ const propertyDefinition = scope.block.parent;
71
+
72
+ return propertyDefinition.static;
73
+ }
74
+
75
+ return false;
69
76
  }
70
77
 
71
78
  /**
72
- * Checks whether or not a given location is inside of the range of a given node.
73
- * @param {ASTNode} node An node to check.
74
- * @param {number} location A location to check.
75
- * @returns {boolean} `true` if the location is inside of the range of the node.
79
+ * Checks whether a given reference is evaluated in an execution context
80
+ * that isn't the one where the variable it refers to is defined.
81
+ * Execution contexts are:
82
+ * - top-level
83
+ * - functions
84
+ * - class field initializers (implicit functions)
85
+ * Static class field initializers are automatically run during the class definition evaluation,
86
+ * and therefore we'll consider them as a part of the parent execution context.
87
+ * Example:
88
+ *
89
+ * const x = 1;
90
+ *
91
+ * x; // returns `false`
92
+ * () => x; // returns `true`
93
+ * class C {
94
+ * field = x; // returns `true`
95
+ * static field = x; // returns `false`
96
+ *
97
+ * method() {
98
+ * x; // returns `true`
99
+ * }
100
+ * }
101
+ * @param {eslint-scope.Reference} reference A reference to check.
102
+ * @returns {boolean} `true` if the reference is from a separate execution context.
76
103
  */
77
- function isInRange(node, location) {
78
- return node && node.range[0] <= location && location <= node.range[1];
104
+ function isFromSeparateExecutionContext(reference) {
105
+ const variable = reference.resolved;
106
+ let scope = reference.from;
107
+
108
+ // Scope#variableScope represents execution context
109
+ while (variable.scope.variableScope !== scope.variableScope) {
110
+ if (isClassStaticInitializerScope(scope.variableScope)) {
111
+ scope = scope.variableScope.upper;
112
+ } else {
113
+ return true;
114
+ }
115
+ }
116
+
117
+ return false;
79
118
  }
80
119
 
81
120
  /**
82
- * Checks whether or not a given reference is inside of the initializers of a given variable.
121
+ * Checks whether or not a given reference is evaluated during the initialization of its variable.
83
122
  *
84
123
  * This returns `true` in the following cases:
85
124
  *
@@ -88,17 +127,44 @@ function isInRange(node, location) {
88
127
  * var {a = a} = obj
89
128
  * for (var a in a) {}
90
129
  * for (var a of a) {}
91
- * @param {Variable} variable A variable to check.
130
+ * var C = class { [C]; }
131
+ * var C = class { static foo = C; }
132
+ * class C extends C {}
133
+ * class C extends (class { static foo = C; }) {}
134
+ * class C { [C]; }
92
135
  * @param {Reference} reference A reference to check.
93
- * @returns {boolean} `true` if the reference is inside of the initializers.
136
+ * @returns {boolean} `true` if the reference is evaluated during the initialization.
94
137
  */
95
- function isInInitializer(variable, reference) {
96
- if (variable.scope !== reference.from) {
138
+ function isEvaluatedDuringInitialization(reference) {
139
+ if (isFromSeparateExecutionContext(reference)) {
140
+
141
+ /*
142
+ * Even if the reference appears in the initializer, it isn't evaluated during the initialization.
143
+ * For example, `const x = () => x;` is valid.
144
+ */
97
145
  return false;
98
146
  }
99
147
 
100
- let node = variable.identifiers[0].parent;
101
148
  const location = reference.identifier.range[1];
149
+ const definition = reference.resolved.defs[0];
150
+
151
+ if (definition.type === "ClassName") {
152
+
153
+ // `ClassDeclaration` or `ClassExpression`
154
+ const classDefinition = definition.node;
155
+
156
+ return (
157
+ isInRange(classDefinition, location) &&
158
+
159
+ /*
160
+ * Class binding is initialized before running static initializers.
161
+ * For example, `class C { static foo = C; }` is valid.
162
+ */
163
+ !isInClassStaticInitializerRange(classDefinition.body, location)
164
+ );
165
+ }
166
+
167
+ let node = definition.name.parent;
102
168
 
103
169
  while (node) {
104
170
  if (node.type === "VariableDeclarator") {
@@ -167,65 +233,77 @@ module.exports = {
167
233
  const options = parseOptions(context.options[0]);
168
234
 
169
235
  /**
170
- * Determines whether a given use-before-define case should be reported according to the options.
171
- * @param {eslint-scope.Variable} variable The variable that gets used before being defined
172
- * @param {eslint-scope.Reference} reference The reference to the variable
173
- * @returns {boolean} `true` if the usage should be reported
236
+ * Determines whether a given reference should be checked.
237
+ *
238
+ * Returns `false` if the reference is:
239
+ * - initialization's (e.g., `let a = 1`).
240
+ * - referring to an undefined variable (i.e., if it's an unresolved reference).
241
+ * - referring to a variable that is defined, but not in the given source code
242
+ * (e.g., global environment variable or `arguments` in functions).
243
+ * - allowed by options.
244
+ * @param {eslint-scope.Reference} reference The reference
245
+ * @returns {boolean} `true` if the reference should be checked
174
246
  */
175
- function isForbidden(variable, reference) {
176
- if (isFunction(variable)) {
177
- return options.functions;
247
+ function shouldCheck(reference) {
248
+ if (reference.init) {
249
+ return false;
250
+ }
251
+
252
+ const variable = reference.resolved;
253
+
254
+ if (!variable || variable.defs.length === 0) {
255
+ return false;
178
256
  }
179
- if (isOuterClass(variable, reference)) {
180
- return options.classes;
257
+
258
+ const definitionType = variable.defs[0].type;
259
+
260
+ if (!options.functions && definitionType === "FunctionName") {
261
+ return false;
181
262
  }
182
- if (isOuterVariable(variable, reference)) {
183
- return options.variables;
263
+
264
+ if (
265
+ (
266
+ !options.variables && definitionType === "Variable" ||
267
+ !options.classes && definitionType === "ClassName"
268
+ ) &&
269
+
270
+ // don't skip checking the reference if it's in the same execution context, because of TDZ
271
+ isFromSeparateExecutionContext(reference)
272
+ ) {
273
+ return false;
184
274
  }
275
+
185
276
  return true;
186
277
  }
187
278
 
188
279
  /**
189
- * Finds and validates all variables in a given scope.
190
- * @param {Scope} scope The scope object.
280
+ * Finds and validates all references in a given scope and its child scopes.
281
+ * @param {eslint-scope.Scope} scope The scope object.
191
282
  * @returns {void}
192
- * @private
193
283
  */
194
- function findVariablesInScope(scope) {
195
- scope.references.forEach(reference => {
284
+ function checkReferencesInScope(scope) {
285
+ scope.references.filter(shouldCheck).forEach(reference => {
196
286
  const variable = reference.resolved;
287
+ const definitionIdentifier = variable.defs[0].name;
197
288
 
198
- /*
199
- * Skips when the reference is:
200
- * - initialization's.
201
- * - referring to an undefined variable.
202
- * - referring to a global environment variable (there're no identifiers).
203
- * - located preceded by the variable (except in initializers).
204
- * - allowed by options.
205
- */
206
- if (reference.init ||
207
- !variable ||
208
- variable.identifiers.length === 0 ||
209
- (variable.identifiers[0].range[1] < reference.identifier.range[1] && !isInInitializer(variable, reference)) ||
210
- !isForbidden(variable, reference)
289
+ if (
290
+ reference.identifier.range[1] < definitionIdentifier.range[1] ||
291
+ isEvaluatedDuringInitialization(reference)
211
292
  ) {
212
- return;
293
+ context.report({
294
+ node: reference.identifier,
295
+ messageId: "usedBeforeDefined",
296
+ data: reference.identifier
297
+ });
213
298
  }
214
-
215
- // Reports.
216
- context.report({
217
- node: reference.identifier,
218
- messageId: "usedBeforeDefined",
219
- data: reference.identifier
220
- });
221
299
  });
222
300
 
223
- scope.childScopes.forEach(findVariablesInScope);
301
+ scope.childScopes.forEach(checkReferencesInScope);
224
302
  }
225
303
 
226
304
  return {
227
305
  Program() {
228
- findVariablesInScope(context.getScope());
306
+ checkReferencesInScope(context.getScope());
229
307
  }
230
308
  };
231
309
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "homepage": "https://eslint.org",
48
48
  "bugs": "https://github.com/eslint/eslint/issues/",
49
49
  "dependencies": {
50
- "@eslint/eslintrc": "^1.0.3",
50
+ "@eslint/eslintrc": "^1.0.4",
51
51
  "@humanwhocodes/config-array": "^0.6.0",
52
52
  "ajv": "^6.10.0",
53
53
  "chalk": "^4.0.0",
@@ -81,7 +81,7 @@
81
81
  "progress": "^2.0.0",
82
82
  "regexpp": "^3.2.0",
83
83
  "semver": "^7.2.1",
84
- "strip-ansi": "^6.0.0",
84
+ "strip-ansi": "^6.0.1",
85
85
  "strip-json-comments": "^3.1.0",
86
86
  "text-table": "^0.2.0",
87
87
  "v8-compile-cache": "^2.0.3"
@@ -101,9 +101,9 @@
101
101
  "eslint-plugin-eslint-comments": "^3.2.0",
102
102
  "eslint-plugin-eslint-plugin": "^4.0.1",
103
103
  "eslint-plugin-internal-rules": "file:tools/internal-rules",
104
- "eslint-plugin-jsdoc": "^36.0.6",
104
+ "eslint-plugin-jsdoc": "^37.0.0",
105
105
  "eslint-plugin-node": "^11.1.0",
106
- "eslint-release": "^3.1.2",
106
+ "eslint-release": "^3.2.0",
107
107
  "eslump": "^3.0.0",
108
108
  "esprima": "^4.0.1",
109
109
  "fs-teardown": "^0.1.3",