eslint 7.5.0 → 7.8.1

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +26 -16
  3. package/conf/config-schema.js +12 -0
  4. package/lib/cli-engine/cascading-config-array-factory.js +12 -0
  5. package/lib/cli-engine/cli-engine.js +2 -2
  6. package/lib/cli-engine/config-array/config-array.js +12 -0
  7. package/lib/cli-engine/config-array/config-dependency.js +12 -0
  8. package/lib/cli-engine/config-array/extracted-config.js +12 -0
  9. package/lib/cli-engine/config-array/ignore-pattern.js +12 -0
  10. package/lib/cli-engine/config-array/index.js +12 -0
  11. package/lib/cli-engine/config-array/override-tester.js +12 -0
  12. package/lib/cli-engine/config-array-factory.js +13 -1
  13. package/lib/cli-engine/formatters/checkstyle.js +2 -2
  14. package/lib/eslint/eslint.js +7 -1
  15. package/lib/init/autoconfig.js +1 -1
  16. package/lib/init/config-initializer.js +3 -3
  17. package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
  18. package/lib/linter/code-path-analysis/code-path-state.js +2 -2
  19. package/lib/linter/config-comment-parser.js +1 -1
  20. package/lib/linter/linter.js +6 -3
  21. package/lib/rule-tester/rule-tester.js +10 -0
  22. package/lib/rules/comma-dangle.js +1 -2
  23. package/lib/rules/constructor-super.js +17 -1
  24. package/lib/rules/id-length.js +19 -1
  25. package/lib/rules/indent.js +4 -3
  26. package/lib/rules/no-duplicate-case.js +23 -4
  27. package/lib/rules/no-loss-of-precision.js +10 -2
  28. package/lib/rules/no-magic-numbers.js +20 -3
  29. package/lib/rules/no-underscore-dangle.js +66 -21
  30. package/lib/rules/no-warning-comments.js +40 -7
  31. package/lib/rules/operator-assignment.js +1 -1
  32. package/lib/rules/prefer-numeric-literals.js +10 -0
  33. package/lib/rules/utils/ast-utils.js +47 -13
  34. package/lib/shared/config-validator.js +14 -2
  35. package/lib/shared/relative-module-resolver.js +12 -0
  36. package/lib/shared/types.js +1 -1
  37. package/messages/extend-config-missing.txt +1 -1
  38. package/messages/no-config-found.txt +1 -1
  39. package/messages/plugin-conflict.txt +1 -1
  40. package/messages/plugin-missing.txt +1 -1
  41. package/messages/whitespace-found.txt +1 -1
  42. package/package.json +3 -3
  43. package/conf/environments.js +0 -168
  44. package/lib/shared/config-ops.js +0 -130
  45. package/lib/shared/naming.js +0 -97
@@ -39,6 +39,13 @@ module.exports = {
39
39
  type: "string"
40
40
  }
41
41
  },
42
+ exceptionPatterns: {
43
+ type: "array",
44
+ uniqueItems: true,
45
+ items: {
46
+ type: "string"
47
+ }
48
+ },
42
49
  properties: {
43
50
  enum: ["always", "never"]
44
51
  }
@@ -63,8 +70,19 @@ module.exports = {
63
70
 
64
71
  return obj;
65
72
  }, {});
73
+ const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
66
74
  const reportedNode = new Set();
67
75
 
76
+ /**
77
+ * Checks if a string matches the provided exception patterns
78
+ * @param {string} name The string to check.
79
+ * @returns {boolean} if the string is a match
80
+ * @private
81
+ */
82
+ function matchesExceptionPattern(name) {
83
+ return exceptionPatterns.some(pattern => pattern.test(name));
84
+ }
85
+
68
86
  const SUPPORTED_EXPRESSIONS = {
69
87
  MemberExpression: properties && function(parent) {
70
88
  return !parent.computed && (
@@ -112,7 +130,7 @@ module.exports = {
112
130
  const isShort = name.length < minLength;
113
131
  const isLong = name.length > maxLength;
114
132
 
115
- if (!(isShort || isLong) || exceptions[name]) {
133
+ if (!(isShort || isLong) || exceptions[name] || matchesExceptionPattern(name)) {
116
134
  return; // Nothing to report
117
135
  }
118
136
 
@@ -1084,16 +1084,17 @@ module.exports = {
1084
1084
  },
1085
1085
 
1086
1086
  ArrowFunctionExpression(node) {
1087
- const firstToken = sourceCode.getFirstToken(node);
1087
+ const maybeOpeningParen = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
1088
1088
 
1089
- if (astUtils.isOpeningParenToken(firstToken)) {
1090
- const openingParen = firstToken;
1089
+ if (astUtils.isOpeningParenToken(maybeOpeningParen)) {
1090
+ const openingParen = maybeOpeningParen;
1091
1091
  const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
1092
1092
 
1093
1093
  parameterParens.add(openingParen);
1094
1094
  parameterParens.add(closingParen);
1095
1095
  addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
1096
1096
  }
1097
+
1097
1098
  addBlocklessNodeIndent(node.body);
1098
1099
  },
1099
1100
 
@@ -6,6 +6,12 @@
6
6
 
7
7
  "use strict";
8
8
 
9
+ //------------------------------------------------------------------------------
10
+ // Requirements
11
+ //------------------------------------------------------------------------------
12
+
13
+ const astUtils = require("./utils/ast-utils");
14
+
9
15
  //------------------------------------------------------------------------------
10
16
  // Rule Definition
11
17
  //------------------------------------------------------------------------------
@@ -31,18 +37,31 @@ module.exports = {
31
37
  create(context) {
32
38
  const sourceCode = context.getSourceCode();
33
39
 
40
+ /**
41
+ * Determines whether the two given nodes are considered to be equal.
42
+ * @param {ASTNode} a First node.
43
+ * @param {ASTNode} b Second node.
44
+ * @returns {boolean} `true` if the nodes are considered to be equal.
45
+ */
46
+ function equal(a, b) {
47
+ if (a.type !== b.type) {
48
+ return false;
49
+ }
50
+
51
+ return astUtils.equalTokens(a, b, sourceCode);
52
+ }
34
53
  return {
35
54
  SwitchStatement(node) {
36
- const previousKeys = new Set();
55
+ const previousTests = [];
37
56
 
38
57
  for (const switchCase of node.cases) {
39
58
  if (switchCase.test) {
40
- const key = sourceCode.getText(switchCase.test);
59
+ const test = switchCase.test;
41
60
 
42
- if (previousKeys.has(key)) {
61
+ if (previousTests.some(previousTest => equal(previousTest, test))) {
43
62
  context.report({ node: switchCase, messageId: "unexpected" });
44
63
  } else {
45
- previousKeys.add(key);
64
+ previousTests.push(test);
46
65
  }
47
66
  }
48
67
  }
@@ -36,6 +36,14 @@ module.exports = {
36
36
  return typeof node.value === "number";
37
37
  }
38
38
 
39
+ /**
40
+ * Gets the source code of the given number literal. Removes `_` numeric separators from the result.
41
+ * @param {Node} node the number `Literal` node
42
+ * @returns {string} raw source code of the literal, without numeric separators
43
+ */
44
+ function getRaw(node) {
45
+ return node.raw.replace(/_/gu, "");
46
+ }
39
47
 
40
48
  /**
41
49
  * Checks whether the number is base ten
@@ -55,7 +63,7 @@ module.exports = {
55
63
  * @returns {boolean} true if they do not match
56
64
  */
57
65
  function notBaseTenLosesPrecision(node) {
58
- const rawString = node.raw.toUpperCase();
66
+ const rawString = getRaw(node).toUpperCase();
59
67
  let base = 0;
60
68
 
61
69
  if (rawString.startsWith("0B")) {
@@ -161,7 +169,7 @@ module.exports = {
161
169
  * @returns {boolean} true if they do not match
162
170
  */
163
171
  function baseTenLosesPrecision(node) {
164
- const normalizedRawNumber = convertNumberToScientificNotation(node.raw);
172
+ const normalizedRawNumber = convertNumberToScientificNotation(getRaw(node));
165
173
  const requestedPrecision = normalizedRawNumber.split("e")[0].replace(".", "").length;
166
174
 
167
175
  if (requestedPrecision > 100) {
@@ -61,6 +61,10 @@ module.exports = {
61
61
  ignoreArrayIndexes: {
62
62
  type: "boolean",
63
63
  default: false
64
+ },
65
+ ignoreDefaultValues: {
66
+ type: "boolean",
67
+ default: false
64
68
  }
65
69
  },
66
70
  additionalProperties: false
@@ -77,7 +81,8 @@ module.exports = {
77
81
  detectObjects = !!config.detectObjects,
78
82
  enforceConst = !!config.enforceConst,
79
83
  ignore = (config.ignore || []).map(normalizeIgnoreValue),
80
- ignoreArrayIndexes = !!config.ignoreArrayIndexes;
84
+ ignoreArrayIndexes = !!config.ignoreArrayIndexes,
85
+ ignoreDefaultValues = !!config.ignoreDefaultValues;
81
86
 
82
87
  const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
83
88
 
@@ -90,6 +95,17 @@ module.exports = {
90
95
  return ignore.indexOf(value) !== -1;
91
96
  }
92
97
 
98
+ /**
99
+ * Returns whether the number is a default value assignment.
100
+ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
101
+ * @returns {boolean} true if the number is a default value
102
+ */
103
+ function isDefaultValue(fullNumberNode) {
104
+ const parent = fullNumberNode.parent;
105
+
106
+ return parent.type === "AssignmentPattern" && parent.right === fullNumberNode;
107
+ }
108
+
93
109
  /**
94
110
  * Returns whether the given node is used as a radix within parseInt() or Number.parseInt()
95
111
  * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
@@ -172,9 +188,12 @@ module.exports = {
172
188
  raw = node.raw;
173
189
  }
174
190
 
191
+ const parent = fullNumberNode.parent;
192
+
175
193
  // Always allow radix arguments and JSX props
176
194
  if (
177
195
  isIgnoredValue(value) ||
196
+ (ignoreDefaultValues && isDefaultValue(fullNumberNode)) ||
178
197
  isParseIntRadix(fullNumberNode) ||
179
198
  isJSXNumber(fullNumberNode) ||
180
199
  (ignoreArrayIndexes && isArrayIndex(fullNumberNode, value))
@@ -182,8 +201,6 @@ module.exports = {
182
201
  return;
183
202
  }
184
203
 
185
- const parent = fullNumberNode.parent;
186
-
187
204
  if (parent.type === "VariableDeclarator") {
188
205
  if (enforceConst && parent.parent.kind !== "const") {
189
206
  context.report({
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Rule to flag trailing underscores in variable declarations.
2
+ * @fileoverview Rule to flag dangling underscores in variable declarations.
3
3
  * @author Matt DuVall <http://www.mattduvall.com>
4
4
  */
5
5
 
@@ -45,6 +45,10 @@ module.exports = {
45
45
  enforceInMethodNames: {
46
46
  type: "boolean",
47
47
  default: false
48
+ },
49
+ allowFunctionParams: {
50
+ type: "boolean",
51
+ default: true
48
52
  }
49
53
  },
50
54
  additionalProperties: false
@@ -64,6 +68,7 @@ module.exports = {
64
68
  const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
65
69
  const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false;
66
70
  const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
71
+ const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
67
72
 
68
73
  //-------------------------------------------------------------------------
69
74
  // Helpers
@@ -80,12 +85,12 @@ module.exports = {
80
85
  }
81
86
 
82
87
  /**
83
- * Check if identifier has a underscore at the end
88
+ * Check if identifier has a dangling underscore
84
89
  * @param {string} identifier name of the node
85
90
  * @returns {boolean} true if its is present
86
91
  * @private
87
92
  */
88
- function hasTrailingUnderscore(identifier) {
93
+ function hasDanglingUnderscore(identifier) {
89
94
  const len = identifier.length;
90
95
 
91
96
  return identifier !== "_" && (identifier[0] === "_" || identifier[len - 1] === "_");
@@ -126,16 +131,53 @@ module.exports = {
126
131
  }
127
132
 
128
133
  /**
129
- * Check if function has a underscore at the end
134
+ * Check if function parameter has a dangling underscore.
135
+ * @param {ASTNode} node function node to evaluate
136
+ * @returns {void}
137
+ * @private
138
+ */
139
+ function checkForDanglingUnderscoreInFunctionParameters(node) {
140
+ if (!allowFunctionParams) {
141
+ node.params.forEach(param => {
142
+ const { type } = param;
143
+ let nodeToCheck;
144
+
145
+ if (type === "RestElement") {
146
+ nodeToCheck = param.argument;
147
+ } else if (type === "AssignmentPattern") {
148
+ nodeToCheck = param.left;
149
+ } else {
150
+ nodeToCheck = param;
151
+ }
152
+
153
+ if (nodeToCheck.type === "Identifier") {
154
+ const identifier = nodeToCheck.name;
155
+
156
+ if (hasDanglingUnderscore(identifier) && !isAllowed(identifier)) {
157
+ context.report({
158
+ node: param,
159
+ messageId: "unexpectedUnderscore",
160
+ data: {
161
+ identifier
162
+ }
163
+ });
164
+ }
165
+ }
166
+ });
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Check if function has a dangling underscore
130
172
  * @param {ASTNode} node node to evaluate
131
173
  * @returns {void}
132
174
  * @private
133
175
  */
134
- function checkForTrailingUnderscoreInFunctionDeclaration(node) {
135
- if (node.id) {
176
+ function checkForDanglingUnderscoreInFunction(node) {
177
+ if (node.type === "FunctionDeclaration" && node.id) {
136
178
  const identifier = node.id.name;
137
179
 
138
- if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
180
+ if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) && !isAllowed(identifier)) {
139
181
  context.report({
140
182
  node,
141
183
  messageId: "unexpectedUnderscore",
@@ -145,18 +187,19 @@ module.exports = {
145
187
  });
146
188
  }
147
189
  }
190
+ checkForDanglingUnderscoreInFunctionParameters(node);
148
191
  }
149
192
 
150
193
  /**
151
- * Check if variable expression has a underscore at the end
194
+ * Check if variable expression has a dangling underscore
152
195
  * @param {ASTNode} node node to evaluate
153
196
  * @returns {void}
154
197
  * @private
155
198
  */
156
- function checkForTrailingUnderscoreInVariableExpression(node) {
199
+ function checkForDanglingUnderscoreInVariableExpression(node) {
157
200
  const identifier = node.id.name;
158
201
 
159
- if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
202
+ if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
160
203
  !isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
161
204
  context.report({
162
205
  node,
@@ -169,18 +212,18 @@ module.exports = {
169
212
  }
170
213
 
171
214
  /**
172
- * Check if member expression has a underscore at the end
215
+ * Check if member expression has a dangling underscore
173
216
  * @param {ASTNode} node node to evaluate
174
217
  * @returns {void}
175
218
  * @private
176
219
  */
177
- function checkForTrailingUnderscoreInMemberExpression(node) {
220
+ function checkForDanglingUnderscoreInMemberExpression(node) {
178
221
  const identifier = node.property.name,
179
222
  isMemberOfThis = node.object.type === "ThisExpression",
180
223
  isMemberOfSuper = node.object.type === "Super",
181
224
  isMemberOfThisConstructor = isThisConstructorReference(node);
182
225
 
183
- if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) &&
226
+ if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
184
227
  !(isMemberOfThis && allowAfterThis) &&
185
228
  !(isMemberOfSuper && allowAfterSuper) &&
186
229
  !(isMemberOfThisConstructor && allowAfterThisConstructor) &&
@@ -196,16 +239,16 @@ module.exports = {
196
239
  }
197
240
 
198
241
  /**
199
- * Check if method declaration or method property has a underscore at the end
242
+ * Check if method declaration or method property has a dangling underscore
200
243
  * @param {ASTNode} node node to evaluate
201
244
  * @returns {void}
202
245
  * @private
203
246
  */
204
- function checkForTrailingUnderscoreInMethod(node) {
247
+ function checkForDanglingUnderscoreInMethod(node) {
205
248
  const identifier = node.key.name;
206
249
  const isMethod = node.type === "MethodDefinition" || node.type === "Property" && node.method;
207
250
 
208
- if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasTrailingUnderscore(identifier) && !isAllowed(identifier)) {
251
+ if (typeof identifier !== "undefined" && enforceInMethodNames && isMethod && hasDanglingUnderscore(identifier) && !isAllowed(identifier)) {
209
252
  context.report({
210
253
  node,
211
254
  messageId: "unexpectedUnderscore",
@@ -221,11 +264,13 @@ module.exports = {
221
264
  //--------------------------------------------------------------------------
222
265
 
223
266
  return {
224
- FunctionDeclaration: checkForTrailingUnderscoreInFunctionDeclaration,
225
- VariableDeclarator: checkForTrailingUnderscoreInVariableExpression,
226
- MemberExpression: checkForTrailingUnderscoreInMemberExpression,
227
- MethodDefinition: checkForTrailingUnderscoreInMethod,
228
- Property: checkForTrailingUnderscoreInMethod
267
+ FunctionDeclaration: checkForDanglingUnderscoreInFunction,
268
+ VariableDeclarator: checkForDanglingUnderscoreInVariableExpression,
269
+ MemberExpression: checkForDanglingUnderscoreInMemberExpression,
270
+ MethodDefinition: checkForDanglingUnderscoreInMethod,
271
+ Property: checkForDanglingUnderscoreInMethod,
272
+ FunctionExpression: checkForDanglingUnderscoreInFunction,
273
+ ArrowFunctionExpression: checkForDanglingUnderscoreInFunction
229
274
  };
230
275
 
231
276
  }
@@ -8,6 +8,8 @@
8
8
  const { escapeRegExp } = require("lodash");
9
9
  const astUtils = require("./utils/ast-utils");
10
10
 
11
+ const CHAR_LIMIT = 40;
12
+
11
13
  //------------------------------------------------------------------------------
12
14
  // Rule Definition
13
15
  //------------------------------------------------------------------------------
@@ -42,12 +44,11 @@ module.exports = {
42
44
  ],
43
45
 
44
46
  messages: {
45
- unexpectedComment: "Unexpected '{{matchedTerm}}' comment."
47
+ unexpectedComment: "Unexpected '{{matchedTerm}}' comment: '{{comment}}'."
46
48
  }
47
49
  },
48
50
 
49
51
  create(context) {
50
-
51
52
  const sourceCode = context.getSourceCode(),
52
53
  configuration = context.options[0] || {},
53
54
  warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
@@ -107,7 +108,15 @@ module.exports = {
107
108
  * \bTERM\b|\bTERM\b, this checks the entire comment
108
109
  * for the term.
109
110
  */
110
- return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "iu");
111
+ return new RegExp(
112
+ prefix +
113
+ escaped +
114
+ suffix +
115
+ eitherOrWordBoundary +
116
+ term +
117
+ wordBoundary,
118
+ "iu"
119
+ );
111
120
  }
112
121
 
113
122
  const warningRegExps = warningTerms.map(convertToRegExp);
@@ -135,18 +144,40 @@ module.exports = {
135
144
  * @returns {void} undefined.
136
145
  */
137
146
  function checkComment(node) {
138
- if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) {
147
+ const comment = node.value;
148
+
149
+ if (
150
+ astUtils.isDirectiveComment(node) &&
151
+ selfConfigRegEx.test(comment)
152
+ ) {
139
153
  return;
140
154
  }
141
155
 
142
- const matches = commentContainsWarningTerm(node.value);
156
+ const matches = commentContainsWarningTerm(comment);
143
157
 
144
158
  matches.forEach(matchedTerm => {
159
+ let commentToDisplay = "";
160
+ let truncated = false;
161
+
162
+ for (const c of comment.trim().split(/\s+/u)) {
163
+ const tmp = commentToDisplay ? `${commentToDisplay} ${c}` : c;
164
+
165
+ if (tmp.length <= CHAR_LIMIT) {
166
+ commentToDisplay = tmp;
167
+ } else {
168
+ truncated = true;
169
+ break;
170
+ }
171
+ }
172
+
145
173
  context.report({
146
174
  node,
147
175
  messageId: "unexpectedComment",
148
176
  data: {
149
- matchedTerm
177
+ matchedTerm,
178
+ comment: `${commentToDisplay}${
179
+ truncated ? "..." : ""
180
+ }`
150
181
  }
151
182
  });
152
183
  });
@@ -156,7 +187,9 @@ module.exports = {
156
187
  Program() {
157
188
  const comments = sourceCode.getAllComments();
158
189
 
159
- comments.filter(token => token.type !== "Shebang").forEach(checkComment);
190
+ comments
191
+ .filter(token => token.type !== "Shebang")
192
+ .forEach(checkComment);
160
193
  }
161
194
  };
162
195
  }
@@ -151,7 +151,7 @@ module.exports = {
151
151
  * @returns {void}
152
152
  */
153
153
  function prohibit(node) {
154
- if (node.operator !== "=") {
154
+ if (node.operator !== "=" && !astUtils.isLogicalAssignmentOperator(node.operator)) {
155
155
  context.report({
156
156
  node,
157
157
  messageId: "unexpected",
@@ -103,6 +103,16 @@ module.exports = {
103
103
  /*
104
104
  * If the newly-produced literal would be invalid, (e.g. 0b1234),
105
105
  * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
106
+ *
107
+ * If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`
108
+ * per the specification doesn't support numeric separators. Thus, the above condition will be `true`
109
+ * (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.
110
+ * Consequently, no autofixes will be made. This is correct behavior because `parseInt` also
111
+ * doesn't support numeric separators, but it does parse part of the string before the first `_`,
112
+ * so the autofix would be invalid:
113
+ *
114
+ * parseInt("1_1", 2) // === 1
115
+ * 0b1_1 // === 3
106
116
  */
107
117
  return null;
108
118
  }
@@ -37,9 +37,11 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
37
37
  // A set of node types that can contain a list of statements
38
38
  const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
39
39
 
40
- const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
40
+ const DECIMAL_INTEGER_PATTERN = /^(0|[1-9](?:_?\d)*)$/u;
41
41
  const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
42
42
 
43
+ const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
44
+
43
45
  /**
44
46
  * Checks reference if is non initializer and writable.
45
47
  * @param {Reference} reference A reference to check.
@@ -722,6 +724,15 @@ function isMixedLogicalAndCoalesceExpressions(left, right) {
722
724
  );
723
725
  }
724
726
 
727
+ /**
728
+ * Checks if the given operator is a logical assignment operator.
729
+ * @param {string} operator The operator to check.
730
+ * @returns {boolean} `true` if the operator is a logical assignment operator.
731
+ */
732
+ function isLogicalAssignmentOperator(operator) {
733
+ return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
734
+ }
735
+
725
736
  //------------------------------------------------------------------------------
726
737
  // Public Interface
727
738
  //------------------------------------------------------------------------------
@@ -1228,16 +1239,25 @@ module.exports = {
1228
1239
  * @returns {boolean} `true` if this node is a decimal integer.
1229
1240
  * @example
1230
1241
  *
1231
- * 5 // true
1232
- * 5. // false
1233
- * 5.0 // false
1234
- * 05 // false
1235
- * 0x5 // false
1236
- * 0b101 // false
1237
- * 0o5 // false
1238
- * 5e0 // false
1239
- * '5' // false
1240
- * 5n // false
1242
+ * 0 // true
1243
+ * 5 // true
1244
+ * 50 // true
1245
+ * 5_000 // true
1246
+ * 1_234_56 // true
1247
+ * 5. // false
1248
+ * .5 // false
1249
+ * 5.0 // false
1250
+ * 5.00_00 // false
1251
+ * 05 // false
1252
+ * 0x5 // false
1253
+ * 0b101 // false
1254
+ * 0b11_01 // false
1255
+ * 0o5 // false
1256
+ * 5e0 // false
1257
+ * 5e1_000 // false
1258
+ * 5n // false
1259
+ * 1_000n // false
1260
+ * '5' // false
1241
1261
  */
1242
1262
  isDecimalInteger(node) {
1243
1263
  return node.type === "Literal" && typeof node.value === "number" &&
@@ -1567,7 +1587,20 @@ module.exports = {
1567
1587
  return true; // possibly an error object.
1568
1588
 
1569
1589
  case "AssignmentExpression":
1570
- return module.exports.couldBeError(node.right);
1590
+ if (["=", "&&="].includes(node.operator)) {
1591
+ return module.exports.couldBeError(node.right);
1592
+ }
1593
+
1594
+ if (["||=", "??="].includes(node.operator)) {
1595
+ return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
1596
+ }
1597
+
1598
+ /**
1599
+ * All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
1600
+ * An assignment expression with a mathematical operator can either evaluate to a primitive value,
1601
+ * or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
1602
+ */
1603
+ return false;
1571
1604
 
1572
1605
  case "SequenceExpression": {
1573
1606
  const exprs = node.expressions;
@@ -1754,5 +1787,6 @@ module.exports = {
1754
1787
  isSpecificId,
1755
1788
  isSpecificMemberAccess,
1756
1789
  equalLiteralValue,
1757
- isSameReference
1790
+ isSameReference,
1791
+ isLogicalAssignmentOperator
1758
1792
  };
@@ -1,3 +1,15 @@
1
+ /*
2
+ * STOP!!! DO NOT MODIFY.
3
+ *
4
+ * This file is part of the ongoing work to move the eslintrc-style config
5
+ * system into the @eslint/eslintrc package. This file needs to remain
6
+ * unchanged in order for this work to proceed.
7
+ *
8
+ * If you think you need to change this file, please contact @nzakas first.
9
+ *
10
+ * Thanks in advance for your cooperation.
11
+ */
12
+
1
13
  /**
2
14
  * @fileoverview Validates configs.
3
15
  * @author Brandon Mills
@@ -12,9 +24,9 @@
12
24
  const
13
25
  util = require("util"),
14
26
  configSchema = require("../../conf/config-schema"),
15
- BuiltInEnvironments = require("../../conf/environments"),
27
+ BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
16
28
  BuiltInRules = require("../rules"),
17
- ConfigOps = require("./config-ops"),
29
+ ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
18
30
  { emitDeprecationWarning } = require("./deprecation-warnings");
19
31
 
20
32
  const ajv = require("./ajv")();
@@ -1,3 +1,15 @@
1
+ /*
2
+ * STOP!!! DO NOT MODIFY.
3
+ *
4
+ * This file is part of the ongoing work to move the eslintrc-style config
5
+ * system into the @eslint/eslintrc package. This file needs to remain
6
+ * unchanged in order for this work to proceed.
7
+ *
8
+ * If you think you need to change this file, please contact @nzakas first.
9
+ *
10
+ * Thanks in advance for your cooperation.
11
+ */
12
+
1
13
  /**
2
14
  * Utility for resolving a module relative to another module
3
15
  * @author Teddy Katz
@@ -21,7 +21,7 @@ module.exports = {};
21
21
  /**
22
22
  * @typedef {Object} ParserOptions
23
23
  * @property {EcmaFeatures} [ecmaFeatures] The optional features.
24
- * @property {3|5|6|7|8|9|10|11|2015|2016|2017|2018|2019|2020} [ecmaVersion] The ECMAScript version (or revision number).
24
+ * @property {3|5|6|7|8|9|10|11|12|2015|2016|2017|2018|2019|2020|2021} [ecmaVersion] The ECMAScript version (or revision number).
25
25
  * @property {"script"|"module"} [sourceType] The source code type.
26
26
  */
27
27
 
@@ -2,4 +2,4 @@ ESLint couldn't find the config "<%- configName %>" to extend from. Please check
2
2
 
3
3
  The config "<%- configName %>" was referenced from the config file in "<%- importerName %>".
4
4
 
5
- If you still have problems, please stop by https://eslint.org/chat to chat with the team.
5
+ If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.