eslint 8.2.0 → 8.3.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
@@ -206,6 +206,9 @@ This means:
206
206
  These folks keep the project moving and are resources for help.
207
207
 
208
208
  <!-- NOTE: This section is autogenerated. Do not manually edit.-->
209
+
210
+
211
+
209
212
  <!--teamstart-->
210
213
 
211
214
  ### Technical Steering Committee (TSC)
@@ -288,6 +291,9 @@ Nitin Kumar
288
291
 
289
292
  <!--teamend-->
290
293
 
294
+
295
+
296
+
291
297
  ## <a name="sponsors"></a>Sponsors
292
298
 
293
299
  The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.
@@ -461,6 +461,10 @@ function processCodePathToEnter(analyzer, node) {
461
461
  startCodePath("function");
462
462
  break;
463
463
 
464
+ case "StaticBlock":
465
+ startCodePath("class-static-block");
466
+ break;
467
+
464
468
  case "ChainExpression":
465
469
  state.pushChainContext();
466
470
  break;
@@ -706,7 +710,8 @@ function postprocess(analyzer, node) {
706
710
  case "Program":
707
711
  case "FunctionDeclaration":
708
712
  case "FunctionExpression":
709
- case "ArrowFunctionExpression": {
713
+ case "ArrowFunctionExpression":
714
+ case "StaticBlock": {
710
715
  endCodePath();
711
716
  break;
712
717
  }
@@ -40,7 +40,7 @@ class CodePath {
40
40
 
41
41
  /**
42
42
  * The reason that this code path was started. May be "program",
43
- * "function", or "class-field-initializer".
43
+ * "function", "class-field-initializer", or "class-static-block".
44
44
  * @type {string}
45
45
  */
46
46
  this.origin = origin;
@@ -626,7 +626,7 @@ function analyzeScope(ast, parserOptions, visitorKeys) {
626
626
  ignoreEval: true,
627
627
  nodejsScope: ecmaFeatures.globalReturn,
628
628
  impliedStrict: ecmaFeatures.impliedStrict,
629
- ecmaVersion,
629
+ ecmaVersion: typeof ecmaVersion === "number" ? ecmaVersion : 6,
630
630
  sourceType: parserOptions.sourceType || "script",
631
631
  childVisitorKeys: visitorKeys || evk.KEYS,
632
632
  fallback: Traverser.getKeys
@@ -112,6 +112,8 @@ module.exports = {
112
112
  "SwitchStatement:exit": exitScope,
113
113
  CatchClause: enterScope,
114
114
  "CatchClause:exit": exitScope,
115
+ StaticBlock: enterScope,
116
+ "StaticBlock:exit": exitScope,
115
117
 
116
118
  // Finds and reports references which are outside of valid scope.
117
119
  VariableDeclaration: checkForVariables
@@ -40,7 +40,7 @@ module.exports = {
40
40
 
41
41
  /**
42
42
  * Gets the open brace token from a given node.
43
- * @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
43
+ * @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to get.
44
44
  * @returns {Token} The token of the open brace.
45
45
  */
46
46
  function getOpenBrace(node) {
@@ -50,6 +50,12 @@ module.exports = {
50
50
  }
51
51
  return sourceCode.getLastToken(node, 1);
52
52
  }
53
+
54
+ if (node.type === "StaticBlock") {
55
+ return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
56
+ }
57
+
58
+ // "BlockStatement"
53
59
  return sourceCode.getFirstToken(node);
54
60
  }
55
61
 
@@ -72,8 +78,8 @@ module.exports = {
72
78
  }
73
79
 
74
80
  /**
75
- * Reports invalid spacing style inside braces.
76
- * @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
81
+ * Checks and reports invalid spacing style inside braces.
82
+ * @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to check.
77
83
  * @returns {void}
78
84
  */
79
85
  function checkSpacingInsideBraces(node) {
@@ -157,6 +163,7 @@ module.exports = {
157
163
 
158
164
  return {
159
165
  BlockStatement: checkSpacingInsideBraces,
166
+ StaticBlock: checkSpacingInsideBraces,
160
167
  SwitchStatement: checkSpacingInsideBraces
161
168
  };
162
169
  }
@@ -155,6 +155,12 @@ module.exports = {
155
155
  validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
156
156
  }
157
157
  },
158
+ StaticBlock(node) {
159
+ validateCurlyPair(
160
+ sourceCode.getFirstToken(node, { skip: 1 }), // skip the `static` token
161
+ sourceCode.getLastToken(node)
162
+ );
163
+ },
158
164
  ClassBody(node) {
159
165
  validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
160
166
  },
@@ -161,8 +161,17 @@ module.exports = {
161
161
  /*
162
162
  * Class field value are implicit functions.
163
163
  */
164
- "PropertyDefinition:exit": popContext,
165
164
  "PropertyDefinition > *.key:exit": pushContext,
165
+ "PropertyDefinition:exit": popContext,
166
+
167
+ /*
168
+ * Class static blocks are implicit functions. They aren't required to use `this`,
169
+ * but we have to push context so that it captures any use of `this` in the static block
170
+ * separately from enclosing contexts, because static blocks have their own `this` and it
171
+ * shouldn't count as used `this` in enclosing contexts.
172
+ */
173
+ StaticBlock: pushContext,
174
+ "StaticBlock:exit": popContext,
166
175
 
167
176
  ThisExpression: markThisUsed,
168
177
  Super: markThisUsed,
@@ -124,20 +124,28 @@ module.exports = {
124
124
 
125
125
  /*
126
126
  * This rule only evaluates complexity of functions, so "program" is excluded.
127
- * Class field initializers are implicit functions. Therefore, they shouldn't contribute
128
- * to the enclosing function's complexity, but their own complexity should be evaluated.
127
+ * Class field initializers and class static blocks are implicit functions. Therefore,
128
+ * they shouldn't contribute to the enclosing function's complexity, but their
129
+ * own complexity should be evaluated.
129
130
  */
130
131
  if (
131
132
  codePath.origin !== "function" &&
132
- codePath.origin !== "class-field-initializer"
133
+ codePath.origin !== "class-field-initializer" &&
134
+ codePath.origin !== "class-static-block"
133
135
  ) {
134
136
  return;
135
137
  }
136
138
 
137
139
  if (complexity > THRESHOLD) {
138
- const name = codePath.origin === "class-field-initializer"
139
- ? "class field initializer"
140
- : astUtils.getFunctionNameWithKind(node);
140
+ let name;
141
+
142
+ if (codePath.origin === "class-field-initializer") {
143
+ name = "class field initializer";
144
+ } else if (codePath.origin === "class-static-block") {
145
+ name = "class static block";
146
+ } else {
147
+ name = astUtils.getFunctionNameWithKind(node);
148
+ }
141
149
 
142
150
  context.report({
143
151
  node,
@@ -68,6 +68,7 @@ const KNOWN_NODES = new Set([
68
68
  "ReturnStatement",
69
69
  "SequenceExpression",
70
70
  "SpreadElement",
71
+ "StaticBlock",
71
72
  "Super",
72
73
  "SwitchCase",
73
74
  "SwitchStatement",
@@ -583,6 +584,16 @@ module.exports = {
583
584
  },
584
585
  additionalProperties: false
585
586
  },
587
+ StaticBlock: {
588
+ type: "object",
589
+ properties: {
590
+ body: {
591
+ type: "integer",
592
+ minimum: 0
593
+ }
594
+ },
595
+ additionalProperties: false
596
+ },
586
597
  CallExpression: {
587
598
  type: "object",
588
599
  properties: {
@@ -646,6 +657,9 @@ module.exports = {
646
657
  parameters: DEFAULT_PARAMETER_INDENT,
647
658
  body: DEFAULT_FUNCTION_BODY_INDENT
648
659
  },
660
+ StaticBlock: {
661
+ body: DEFAULT_FUNCTION_BODY_INDENT
662
+ },
649
663
  CallExpression: {
650
664
  arguments: DEFAULT_PARAMETER_INDENT
651
665
  },
@@ -1397,6 +1411,13 @@ module.exports = {
1397
1411
  }
1398
1412
  },
1399
1413
 
1414
+ StaticBlock(node) {
1415
+ const openingCurly = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
1416
+ const closingCurly = sourceCode.getLastToken(node);
1417
+
1418
+ addElementListIndent(node.body, openingCurly, closingCurly, options.StaticBlock.body);
1419
+ },
1420
+
1400
1421
  SwitchStatement(node) {
1401
1422
  const openingCurly = sourceCode.getTokenAfter(node.discriminant, astUtils.isOpeningBraceToken);
1402
1423
  const closingCurly = sourceCode.getLastToken(node);
@@ -590,6 +590,7 @@ module.exports = {
590
590
  ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
591
591
  MethodDefinition: checkSpacingForProperty,
592
592
  PropertyDefinition: checkSpacingForProperty,
593
+ StaticBlock: checkSpacingAroundFirstToken,
593
594
  Property: checkSpacingForProperty,
594
595
 
595
596
  // To avoid conflicts with `space-infix-ops`, e.g. `a > this.b`
@@ -185,10 +185,39 @@ module.exports = {
185
185
  /**
186
186
  * Returns the parent node that contains the given token.
187
187
  * @param {token} token The token to check.
188
- * @returns {ASTNode} The parent node that contains the given token.
188
+ * @returns {ASTNode|null} The parent node that contains the given token.
189
189
  */
190
190
  function getParentNodeOfToken(token) {
191
- return sourceCode.getNodeByRangeIndex(token.range[0]);
191
+ const node = sourceCode.getNodeByRangeIndex(token.range[0]);
192
+
193
+ /*
194
+ * For the purpose of this rule, the comment token is in a `StaticBlock` node only
195
+ * if it's inside the braces of that `StaticBlock` node.
196
+ *
197
+ * Example where this function returns `null`:
198
+ *
199
+ * static
200
+ * // comment
201
+ * {
202
+ * }
203
+ *
204
+ * Example where this function returns `StaticBlock` node:
205
+ *
206
+ * static
207
+ * {
208
+ * // comment
209
+ * }
210
+ *
211
+ */
212
+ if (node && node.type === "StaticBlock") {
213
+ const openingBrace = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
214
+
215
+ return token.range[0] >= openingBrace.range[0]
216
+ ? node
217
+ : null;
218
+ }
219
+
220
+ return node;
192
221
  }
193
222
 
194
223
  /**
@@ -200,8 +229,15 @@ module.exports = {
200
229
  function isCommentAtParentStart(token, nodeType) {
201
230
  const parent = getParentNodeOfToken(token);
202
231
 
203
- return parent && isParentNodeType(parent, nodeType) &&
204
- token.loc.start.line - parent.loc.start.line === 1;
232
+ if (parent && isParentNodeType(parent, nodeType)) {
233
+ const parentStartNodeOrToken = parent.type === "StaticBlock"
234
+ ? sourceCode.getFirstToken(parent, { skip: 1 }) // opening brace of the static block
235
+ : parent;
236
+
237
+ return token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
238
+ }
239
+
240
+ return false;
205
241
  }
206
242
 
207
243
  /**
@@ -213,7 +249,7 @@ module.exports = {
213
249
  function isCommentAtParentEnd(token, nodeType) {
214
250
  const parent = getParentNodeOfToken(token);
215
251
 
216
- return parent && isParentNodeType(parent, nodeType) &&
252
+ return !!parent && isParentNodeType(parent, nodeType) &&
217
253
  parent.loc.end.line - token.loc.end.line === 1;
218
254
  }
219
255
 
@@ -223,7 +259,12 @@ module.exports = {
223
259
  * @returns {boolean} True if the comment is at block start.
224
260
  */
225
261
  function isCommentAtBlockStart(token) {
226
- return isCommentAtParentStart(token, "ClassBody") || isCommentAtParentStart(token, "BlockStatement") || isCommentAtParentStart(token, "SwitchCase");
262
+ return (
263
+ isCommentAtParentStart(token, "ClassBody") ||
264
+ isCommentAtParentStart(token, "BlockStatement") ||
265
+ isCommentAtParentStart(token, "StaticBlock") ||
266
+ isCommentAtParentStart(token, "SwitchCase")
267
+ );
227
268
  }
228
269
 
229
270
  /**
@@ -232,7 +273,13 @@ module.exports = {
232
273
  * @returns {boolean} True if the comment is at block end.
233
274
  */
234
275
  function isCommentAtBlockEnd(token) {
235
- return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
276
+ return (
277
+ isCommentAtParentEnd(token, "ClassBody") ||
278
+ isCommentAtParentEnd(token, "BlockStatement") ||
279
+ isCommentAtParentEnd(token, "StaticBlock") ||
280
+ isCommentAtParentEnd(token, "SwitchCase") ||
281
+ isCommentAtParentEnd(token, "SwitchStatement")
282
+ );
236
283
  }
237
284
 
238
285
  /**
@@ -118,6 +118,7 @@ module.exports = {
118
118
  FunctionDeclaration: startFunction,
119
119
  FunctionExpression: startFunction,
120
120
  ArrowFunctionExpression: startFunction,
121
+ StaticBlock: startFunction,
121
122
 
122
123
  IfStatement(node) {
123
124
  if (node.parent.type !== "IfStatement") {
@@ -146,6 +147,7 @@ module.exports = {
146
147
  "FunctionDeclaration:exit": endFunction,
147
148
  "FunctionExpression:exit": endFunction,
148
149
  "ArrowFunctionExpression:exit": endFunction,
150
+ "StaticBlock:exit": endFunction,
149
151
  "Program:exit": endFunction
150
152
  };
151
153
 
@@ -123,6 +123,14 @@ module.exports = {
123
123
  function endFunction(node) {
124
124
  const count = functionStack.pop();
125
125
 
126
+ /*
127
+ * This rule does not apply to class static blocks, but we have to track them so
128
+ * that stataments in them do not count as statements in the enclosing function.
129
+ */
130
+ if (node.type === "StaticBlock") {
131
+ return;
132
+ }
133
+
126
134
  if (ignoreTopLevelFunctions && functionStack.length === 0) {
127
135
  topLevelFunctions.push({ node, count });
128
136
  } else {
@@ -148,12 +156,14 @@ module.exports = {
148
156
  FunctionDeclaration: startFunction,
149
157
  FunctionExpression: startFunction,
150
158
  ArrowFunctionExpression: startFunction,
159
+ StaticBlock: startFunction,
151
160
 
152
161
  BlockStatement: countStatements,
153
162
 
154
163
  "FunctionDeclaration:exit": endFunction,
155
164
  "FunctionExpression:exit": endFunction,
156
165
  "ArrowFunctionExpression:exit": endFunction,
166
+ "StaticBlock:exit": endFunction,
157
167
 
158
168
  "Program:exit"() {
159
169
  if (topLevelFunctions.length === 1) {
@@ -248,6 +248,8 @@ module.exports = {
248
248
  "ArrowFunctionExpression:exit": exitVarScope,
249
249
  "PropertyDefinition > *.value": enterVarScope,
250
250
  "PropertyDefinition > *.value:exit": exitVarScope,
251
+ StaticBlock: enterVarScope,
252
+ "StaticBlock:exit": exitVarScope,
251
253
 
252
254
  ThisExpression(node) {
253
255
  if (!isMember(node.parent, "eval")) {
@@ -116,7 +116,7 @@ module.exports = {
116
116
  * @param {Node} node A MethodDefinition node of the start point.
117
117
  * @returns {void}
118
118
  */
119
- "MethodDefinition, PropertyDefinition"(node) {
119
+ "MethodDefinition, PropertyDefinition, StaticBlock"(node) {
120
120
  checkForPartOfClassBody(sourceCode.getTokenAfter(node));
121
121
  }
122
122
  };
@@ -15,9 +15,33 @@ const astUtils = require("./utils/ast-utils");
15
15
  // Rule Definition
16
16
  //------------------------------------------------------------------------------
17
17
 
18
- const validParent = new Set(["Program", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
18
+ const validParent = new Set(["Program", "StaticBlock", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
19
19
  const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
20
20
 
21
+ /**
22
+ * Finds the nearest enclosing context where this rule allows declarations and returns its description.
23
+ * @param {ASTNode} node Node to search from.
24
+ * @returns {string} Description. One of "program", "function body", "class static block body".
25
+ */
26
+ function getAllowedBodyDescription(node) {
27
+ let { parent } = node;
28
+
29
+ while (parent) {
30
+
31
+ if (parent.type === "StaticBlock") {
32
+ return "class static block body";
33
+ }
34
+
35
+ if (astUtils.isFunction(parent)) {
36
+ return "function body";
37
+ }
38
+
39
+ ({ parent } = parent);
40
+ }
41
+
42
+ return "program";
43
+ }
44
+
21
45
  module.exports = {
22
46
  meta: {
23
47
  type: "problem",
@@ -59,14 +83,12 @@ module.exports = {
59
83
  return;
60
84
  }
61
85
 
62
- const upperFunction = astUtils.getUpperFunction(parent);
63
-
64
86
  context.report({
65
87
  node,
66
88
  messageId: "moveDeclToRoot",
67
89
  data: {
68
90
  type: (node.type === "FunctionDeclaration" ? "function" : "variable"),
69
- body: (upperFunction === null ? "program" : "function body")
91
+ body: getAllowedBodyDescription(node)
70
92
  }
71
93
  });
72
94
  }
@@ -132,6 +132,10 @@ module.exports = {
132
132
  "PropertyDefinition > *.value": enterFunction,
133
133
  "PropertyDefinition > *.value:exit": exitFunction,
134
134
 
135
+ // Class static blocks are implicit functions.
136
+ StaticBlock: enterFunction,
137
+ "StaticBlock:exit": exitFunction,
138
+
135
139
  // Reports if `this` of the current context is invalid.
136
140
  ThisExpression(node) {
137
141
  const current = stack.getCurrent();
@@ -39,7 +39,9 @@ module.exports = {
39
39
  * @returns {void}
40
40
  */
41
41
  function report(node) {
42
- const messageId = node.parent.type === "BlockStatement" ? "redundantNestedBlock" : "redundantBlock";
42
+ const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
43
+ ? "redundantNestedBlock"
44
+ : "redundantBlock";
43
45
 
44
46
  context.report({
45
47
  node,
@@ -54,6 +56,7 @@ module.exports = {
54
56
  */
55
57
  function isLoneBlock(node) {
56
58
  return node.parent.type === "BlockStatement" ||
59
+ node.parent.type === "StaticBlock" ||
57
60
  node.parent.type === "Program" ||
58
61
 
59
62
  // Don't report blocks in switch cases if the block is the only statement of the case.
@@ -99,7 +102,10 @@ module.exports = {
99
102
  loneBlocks.pop();
100
103
  report(node);
101
104
  } else if (
102
- node.parent.type === "BlockStatement" &&
105
+ (
106
+ node.parent.type === "BlockStatement" ||
107
+ node.parent.type === "StaticBlock"
108
+ ) &&
103
109
  node.parent.body.length === 1
104
110
  ) {
105
111
  report(node);
@@ -161,6 +161,8 @@ module.exports = {
161
161
  FunctionExpression: checkForBlock,
162
162
  ArrowFunctionExpression: checkForBlock,
163
163
 
164
+ StaticBlock: checkForBlock,
165
+
164
166
  BlockStatement: checkForBlock,
165
167
  ForStatement: checkForBlock,
166
168
  ForInStatement: checkForBlock,
@@ -115,6 +115,12 @@ module.exports = {
115
115
  const parent = ancestors[ancestors.length - 1],
116
116
  grandparent = ancestors[ancestors.length - 2];
117
117
 
118
+ /**
119
+ * https://tc39.es/ecma262/#directive-prologue
120
+ *
121
+ * Only `FunctionBody`, `ScriptBody` and `ModuleBody` can have directive prologue.
122
+ * Class static blocks do not have directive prologue.
123
+ */
118
124
  return (parent.type === "Program" || parent.type === "BlockStatement" &&
119
125
  (/Function/u.test(grandparent.type))) &&
120
126
  directives(parent).indexOf(node) >= 0;
@@ -45,25 +45,37 @@ function isInRange(node, location) {
45
45
 
46
46
  /**
47
47
  * Checks whether or not a given location is inside of the range of a class static initializer.
48
+ * Static initializers are static blocks and initializers of static fields.
48
49
  * @param {ASTNode} node `ClassBody` node to check static initializers.
49
50
  * @param {number} location A location to check.
50
51
  * @returns {boolean} `true` if the location is inside of a class static initializer.
51
52
  */
52
53
  function isInClassStaticInitializerRange(node, location) {
53
54
  return node.body.some(classMember => (
54
- classMember.type === "PropertyDefinition" &&
55
- classMember.static &&
56
- classMember.value &&
57
- isInRange(classMember.value, location)
55
+ (
56
+ classMember.type === "StaticBlock" &&
57
+ isInRange(classMember, location)
58
+ ) ||
59
+ (
60
+ classMember.type === "PropertyDefinition" &&
61
+ classMember.static &&
62
+ classMember.value &&
63
+ isInRange(classMember.value, location)
64
+ )
58
65
  ));
59
66
  }
60
67
 
61
68
  /**
62
- * Checks whether a given scope is the scope of a static class field initializer.
69
+ * Checks whether a given scope is the scope of a a class static initializer.
70
+ * Static initializers are static blocks and initializers of static fields.
63
71
  * @param {eslint-scope.Scope} scope A scope to check.
64
72
  * @returns {boolean} `true` if the scope is a class static initializer scope.
65
73
  */
66
74
  function isClassStaticInitializerScope(scope) {
75
+ if (scope.type === "class-static-block") {
76
+ return true;
77
+ }
78
+
67
79
  if (scope.type === "class-field-initializer") {
68
80
 
69
81
  // `scope.block` is PropertyDefinition#value node
@@ -82,7 +94,8 @@ function isClassStaticInitializerScope(scope) {
82
94
  * - top-level
83
95
  * - functions
84
96
  * - class field initializers (implicit functions)
85
- * Static class field initializers are automatically run during the class definition evaluation,
97
+ * - class static blocks (implicit functions)
98
+ * Static class field initializers and class static blocks are automatically run during the class definition evaluation,
86
99
  * and therefore we'll consider them as a part of the parent execution context.
87
100
  * Example:
88
101
  *
@@ -90,6 +103,7 @@ function isClassStaticInitializerScope(scope) {
90
103
  *
91
104
  * x; // returns `false`
92
105
  * () => x; // returns `true`
106
+ *
93
107
  * class C {
94
108
  * field = x; // returns `true`
95
109
  * static field = x; // returns `false`
@@ -97,6 +111,14 @@ function isClassStaticInitializerScope(scope) {
97
111
  * method() {
98
112
  * x; // returns `true`
99
113
  * }
114
+ *
115
+ * static method() {
116
+ * x; // returns `true`
117
+ * }
118
+ *
119
+ * static {
120
+ * x; // returns `false`
121
+ * }
100
122
  * }
101
123
  * @param {eslint-scope.Reference} reference A reference to check.
102
124
  * @returns {boolean} `true` if the reference is from a separate execution context.
@@ -127,8 +149,9 @@ function isFromSeparateExecutionContext(reference) {
127
149
  * var {a = a} = obj
128
150
  * for (var a in a) {}
129
151
  * for (var a of a) {}
130
- * var C = class { [C]; }
131
- * var C = class { static foo = C; }
152
+ * var C = class { [C]; };
153
+ * var C = class { static foo = C; };
154
+ * var C = class { static { foo = C; } };
132
155
  * class C extends C {}
133
156
  * class C extends (class { static foo = C; }) {}
134
157
  * class C { [C]; }
@@ -158,7 +181,7 @@ function isEvaluatedDuringInitialization(reference) {
158
181
 
159
182
  /*
160
183
  * Class binding is initialized before running static initializers.
161
- * For example, `class C { static foo = C; }` is valid.
184
+ * For example, `class C { static foo = C; static { bar = C; } }` is valid.
162
185
  */
163
186
  !isInClassStaticInitializerRange(classDefinition.body, location)
164
187
  );
@@ -541,6 +541,8 @@ module.exports = {
541
541
  FunctionDeclaration: startFunction,
542
542
  FunctionExpression: startFunction,
543
543
  ArrowFunctionExpression: startFunction,
544
+ StaticBlock: startFunction, // StaticBlock creates a new scope for `var` variables
545
+
544
546
  BlockStatement: startBlock,
545
547
  ForStatement: startBlock,
546
548
  ForInStatement: startBlock,
@@ -552,10 +554,12 @@ module.exports = {
552
554
  "ForInStatement:exit": endBlock,
553
555
  "SwitchStatement:exit": endBlock,
554
556
  "BlockStatement:exit": endBlock,
557
+
555
558
  "Program:exit": endFunction,
556
559
  "FunctionDeclaration:exit": endFunction,
557
560
  "FunctionExpression:exit": endFunction,
558
- "ArrowFunctionExpression:exit": endFunction
561
+ "ArrowFunctionExpression:exit": endFunction,
562
+ "StaticBlock:exit": endFunction
559
563
  };
560
564
 
561
565
  }
@@ -106,6 +106,12 @@ module.exports = {
106
106
  if (node.type === "SwitchStatement") {
107
107
  return sourceCode.getTokenBefore(node.cases[0]);
108
108
  }
109
+
110
+ if (node.type === "StaticBlock") {
111
+ return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
112
+ }
113
+
114
+ // `BlockStatement` or `ClassBody`
109
115
  return sourceCode.getFirstToken(node);
110
116
  }
111
117
 
@@ -172,6 +178,7 @@ module.exports = {
172
178
  function requirePaddingFor(node) {
173
179
  switch (node.type) {
174
180
  case "BlockStatement":
181
+ case "StaticBlock":
175
182
  return options.blocks;
176
183
  case "SwitchStatement":
177
184
  return options.switches;
@@ -282,6 +289,7 @@ module.exports = {
282
289
  }
283
290
  checkPadding(node);
284
291
  };
292
+ rule.StaticBlock = rule.BlockStatement;
285
293
  }
286
294
 
287
295
  if (Object.prototype.hasOwnProperty.call(options, "classes")) {
@@ -618,9 +618,11 @@ module.exports = {
618
618
  Program: enterScope,
619
619
  BlockStatement: enterScope,
620
620
  SwitchStatement: enterScope,
621
+ StaticBlock: enterScope,
621
622
  "Program:exit": exitScope,
622
623
  "BlockStatement:exit": exitScope,
623
624
  "SwitchStatement:exit": exitScope,
625
+ "StaticBlock:exit": exitScope,
624
626
 
625
627
  ":statement": verify,
626
628
 
@@ -17,7 +17,7 @@ const astUtils = require("./utils/ast-utils");
17
17
  //------------------------------------------------------------------------------
18
18
 
19
19
  const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
20
- const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/u;
20
+ const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|StaticBlock|SwitchCase)$/u;
21
21
  const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/u;
22
22
 
23
23
  /**
@@ -176,7 +176,17 @@ module.exports = {
176
176
  },
177
177
 
178
178
  fixable: null,
179
- schema: [],
179
+
180
+ schema: [{
181
+ type: "object",
182
+ properties: {
183
+ allowProperties: {
184
+ type: "boolean",
185
+ default: false
186
+ }
187
+ },
188
+ additionalProperties: false
189
+ }],
180
190
 
181
191
  messages: {
182
192
  nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`.",
@@ -185,6 +195,8 @@ module.exports = {
185
195
  },
186
196
 
187
197
  create(context) {
198
+ const allowProperties = !!context.options[0] && context.options[0].allowProperties;
199
+
188
200
  const sourceCode = context.getSourceCode();
189
201
  const assignmentReferences = new Map();
190
202
  const segmentInfo = new SegmentInfo();
@@ -284,7 +296,7 @@ module.exports = {
284
296
  value: variable.name
285
297
  }
286
298
  });
287
- } else {
299
+ } else if (!allowProperties) {
288
300
  context.report({
289
301
  node: node.parent,
290
302
  messageId: "nonAtomicObjectUpdate",
@@ -25,7 +25,8 @@ const SELECTOR = [
25
25
 
26
26
  /**
27
27
  * Get the child node list of a given node.
28
- * This returns `Program#body`, `BlockStatement#body`, or `SwitchCase#consequent`.
28
+ * This returns `BlockStatement#body`, `StaticBlock#body`, `Program#body`,
29
+ * `ClassBody#body`, or `SwitchCase#consequent`.
29
30
  * This is used to check whether a node is the first/last child.
30
31
  * @param {Node} node A node to get child node list.
31
32
  * @returns {Node[]|null} The child node list.
@@ -33,7 +34,12 @@ const SELECTOR = [
33
34
  function getChildren(node) {
34
35
  const t = node.type;
35
36
 
36
- if (t === "BlockStatement" || t === "Program" || t === "ClassBody") {
37
+ if (
38
+ t === "BlockStatement" ||
39
+ t === "StaticBlock" ||
40
+ t === "Program" ||
41
+ t === "ClassBody"
42
+ ) {
37
43
  return node.body;
38
44
  }
39
45
  if (t === "SwitchCase") {
package/lib/rules/semi.js CHANGED
@@ -306,22 +306,31 @@ module.exports = {
306
306
  }
307
307
 
308
308
  /**
309
- * Checks a node to see if it's in a one-liner block statement.
309
+ * Checks a node to see if it's the last item in a one-liner block.
310
+ * Block is any `BlockStatement` or `StaticBlock` node. Block is a one-liner if its
311
+ * braces (and consequently everything between them) are on the same line.
310
312
  * @param {ASTNode} node The node to check.
311
- * @returns {boolean} whether the node is in a one-liner block statement.
313
+ * @returns {boolean} whether the node is the last item in a one-liner block.
312
314
  */
313
- function isOneLinerBlock(node) {
315
+ function isLastInOneLinerBlock(node) {
314
316
  const parent = node.parent;
315
317
  const nextToken = sourceCode.getTokenAfter(node);
316
318
 
317
319
  if (!nextToken || nextToken.value !== "}") {
318
320
  return false;
319
321
  }
320
- return (
321
- !!parent &&
322
- parent.type === "BlockStatement" &&
323
- parent.loc.start.line === parent.loc.end.line
324
- );
322
+
323
+ if (parent.type === "BlockStatement") {
324
+ return parent.loc.start.line === parent.loc.end.line;
325
+ }
326
+
327
+ if (parent.type === "StaticBlock") {
328
+ const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 }); // skip the `static` token
329
+
330
+ return openingBrace.loc.start.line === parent.loc.end.line;
331
+ }
332
+
333
+ return false;
325
334
  }
326
335
 
327
336
  /**
@@ -343,7 +352,7 @@ module.exports = {
343
352
  report(node);
344
353
  }
345
354
  } else {
346
- const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
355
+ const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
347
356
 
348
357
  if (isSemi && oneLinerBlock) {
349
358
  report(node, true);
@@ -35,7 +35,7 @@ const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|g
35
35
  const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
36
36
 
37
37
  // A set of node types that can contain a list of statements
38
- const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
38
+ const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "StaticBlock", "SwitchCase"]);
39
39
 
40
40
  const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
41
41
 
@@ -937,6 +937,8 @@ module.exports = {
937
937
  *
938
938
  * First, this checks the node:
939
939
  *
940
+ * - The given node is not in `PropertyDefinition#value` position.
941
+ * - The given node is not `StaticBlock`.
940
942
  * - The function name does not start with uppercase. It's a convention to capitalize the names
941
943
  * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`.
942
944
  * - The function does not have a JSDoc comment that has a @this tag.
@@ -951,7 +953,8 @@ module.exports = {
951
953
  * - The location is not on an ES2015 class.
952
954
  * - Its `bind`/`call`/`apply` method is not called directly.
953
955
  * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
954
- * @param {ASTNode} node A function node to check.
956
+ * @param {ASTNode} node A function node to check. It also can be an implicit function, like `StaticBlock`
957
+ * or any expression that is `PropertyDefinition#value` node.
955
958
  * @param {SourceCode} sourceCode A SourceCode instance to get comments.
956
959
  * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts
957
960
  * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors.
@@ -964,7 +967,12 @@ module.exports = {
964
967
  * Therefore, A expression node at `PropertyDefinition#value` is a function.
965
968
  * In this case, `this` is always not default binding.
966
969
  */
967
- if (node && node.parent && node.parent.type === "PropertyDefinition" && node.value === node) {
970
+ if (node.parent.type === "PropertyDefinition" && node.parent.value === node) {
971
+ return false;
972
+ }
973
+
974
+ // Class static blocks are implicit functions. In this case, `this` is always not default binding.
975
+ if (node.type === "StaticBlock") {
968
976
  return false;
969
977
  }
970
978
 
@@ -1825,6 +1833,10 @@ module.exports = {
1825
1833
  return true;
1826
1834
  }
1827
1835
 
1836
+ if (rightToken.type === "PrivateIdentifier") {
1837
+ return true;
1838
+ }
1839
+
1828
1840
  return false;
1829
1841
  },
1830
1842
 
@@ -77,10 +77,12 @@ module.exports = {
77
77
  const l = statements.length;
78
78
  let i = 0;
79
79
 
80
- // skip over directives
81
- for (; i < l; ++i) {
82
- if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
83
- break;
80
+ // Skip over directives and imports. Static blocks don't have either.
81
+ if (node.parent.type !== "StaticBlock") {
82
+ for (; i < l; ++i) {
83
+ if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
84
+ break;
85
+ }
84
86
  }
85
87
  }
86
88
 
@@ -111,16 +113,27 @@ module.exports = {
111
113
  /**
112
114
  * Checks whether variable is on top at functional block scope level
113
115
  * @param {ASTNode} node The node to check
114
- * @param {ASTNode} parent Parent of the node
115
- * @param {ASTNode} grandParent Parent of the node's parent
116
116
  * @returns {void}
117
117
  */
118
- function blockScopeVarCheck(node, parent, grandParent) {
119
- if (!(/Function/u.test(grandParent.type) &&
120
- parent.type === "BlockStatement" &&
121
- isVarOnTop(node, parent.body))) {
122
- context.report({ node, messageId: "top" });
118
+ function blockScopeVarCheck(node) {
119
+ const { parent } = node;
120
+
121
+ if (
122
+ parent.type === "BlockStatement" &&
123
+ /Function/u.test(parent.parent.type) &&
124
+ isVarOnTop(node, parent.body)
125
+ ) {
126
+ return;
123
127
  }
128
+
129
+ if (
130
+ parent.type === "StaticBlock" &&
131
+ isVarOnTop(node, parent.body)
132
+ ) {
133
+ return;
134
+ }
135
+
136
+ context.report({ node, messageId: "top" });
124
137
  }
125
138
 
126
139
  //--------------------------------------------------------------------------
@@ -134,7 +147,7 @@ module.exports = {
134
147
  } else if (node.parent.type === "Program") {
135
148
  globalVarCheck(node, node.parent);
136
149
  } else {
137
- blockScopeVarCheck(node, node.parent, node.parent.parent);
150
+ blockScopeVarCheck(node);
138
151
  }
139
152
  }
140
153
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.2.0",
3
+ "version": "8.3.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -56,10 +56,10 @@
56
56
  "doctrine": "^3.0.0",
57
57
  "enquirer": "^2.3.5",
58
58
  "escape-string-regexp": "^4.0.0",
59
- "eslint-scope": "^6.0.0",
59
+ "eslint-scope": "^7.1.0",
60
60
  "eslint-utils": "^3.0.0",
61
- "eslint-visitor-keys": "^3.0.0",
62
- "espree": "^9.0.0",
61
+ "eslint-visitor-keys": "^3.1.0",
62
+ "espree": "^9.1.0",
63
63
  "esquery": "^1.4.0",
64
64
  "esutils": "^2.0.2",
65
65
  "fast-deep-equal": "^3.1.3",