eslint 8.34.0 → 8.37.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.
Files changed (84) hide show
  1. package/README.md +11 -12
  2. package/conf/rule-type-list.json +2 -2
  3. package/lib/cli-engine/cli-engine.js +2 -2
  4. package/lib/cli-engine/file-enumerator.js +8 -6
  5. package/lib/config/default-config.js +1 -4
  6. package/lib/config/flat-config-array.js +77 -17
  7. package/lib/config/flat-config-schema.js +4 -18
  8. package/lib/eslint/eslint-helpers.js +3 -3
  9. package/lib/linter/linter.js +5 -30
  10. package/lib/rule-tester/flat-rule-tester.js +1 -1
  11. package/lib/rule-tester/rule-tester.js +1 -1
  12. package/lib/rules/camelcase.js +3 -2
  13. package/lib/rules/consistent-this.js +4 -2
  14. package/lib/rules/global-require.js +3 -1
  15. package/lib/rules/handle-callback-err.js +2 -1
  16. package/lib/rules/id-blacklist.js +3 -2
  17. package/lib/rules/id-denylist.js +3 -2
  18. package/lib/rules/id-match.js +3 -2
  19. package/lib/rules/lines-around-comment.js +11 -0
  20. package/lib/rules/logical-assignment-operators.js +4 -4
  21. package/lib/rules/multiline-comment-style.js +42 -3
  22. package/lib/rules/no-alert.js +3 -1
  23. package/lib/rules/no-catch-shadow.js +3 -1
  24. package/lib/rules/no-console.js +3 -2
  25. package/lib/rules/no-constant-binary-expression.js +36 -27
  26. package/lib/rules/no-constant-condition.js +3 -2
  27. package/lib/rules/no-control-regex.js +1 -1
  28. package/lib/rules/no-else-return.js +13 -12
  29. package/lib/rules/no-eval.js +5 -5
  30. package/lib/rules/no-extend-native.js +3 -2
  31. package/lib/rules/no-extra-boolean-cast.js +1 -1
  32. package/lib/rules/no-extra-parens.js +1 -1
  33. package/lib/rules/no-global-assign.js +3 -2
  34. package/lib/rules/no-implicit-globals.js +3 -2
  35. package/lib/rules/no-implied-eval.js +5 -4
  36. package/lib/rules/no-import-assign.js +4 -2
  37. package/lib/rules/no-invalid-regexp.js +1 -1
  38. package/lib/rules/no-invalid-this.js +2 -2
  39. package/lib/rules/no-label-var.js +2 -1
  40. package/lib/rules/no-lone-blocks.js +3 -2
  41. package/lib/rules/no-loop-func.js +3 -1
  42. package/lib/rules/no-misleading-character-class.js +12 -41
  43. package/lib/rules/no-native-reassign.js +3 -2
  44. package/lib/rules/no-new-func.js +7 -6
  45. package/lib/rules/no-new-native-nonconstructor.js +8 -6
  46. package/lib/rules/no-new-object.js +4 -1
  47. package/lib/rules/no-new-symbol.js +8 -6
  48. package/lib/rules/no-obj-calls.js +8 -6
  49. package/lib/rules/no-promise-executor-return.js +3 -2
  50. package/lib/rules/no-redeclare.js +3 -3
  51. package/lib/rules/no-regex-spaces.js +4 -2
  52. package/lib/rules/no-restricted-globals.js +4 -2
  53. package/lib/rules/no-setter-return.js +3 -2
  54. package/lib/rules/no-shadow.js +3 -2
  55. package/lib/rules/no-undef-init.js +1 -1
  56. package/lib/rules/no-undef.js +3 -2
  57. package/lib/rules/no-undefined.js +4 -2
  58. package/lib/rules/no-unmodified-loop-condition.js +2 -2
  59. package/lib/rules/no-unused-vars.js +1 -1
  60. package/lib/rules/no-use-before-define.js +3 -2
  61. package/lib/rules/no-useless-backreference.js +9 -7
  62. package/lib/rules/no-useless-return.js +1 -1
  63. package/lib/rules/object-shorthand.js +3 -2
  64. package/lib/rules/prefer-arrow-callback.js +1 -1
  65. package/lib/rules/prefer-exponentiation-operator.js +5 -5
  66. package/lib/rules/prefer-named-capture-group.js +8 -8
  67. package/lib/rules/prefer-object-has-own.js +4 -2
  68. package/lib/rules/prefer-object-spread.js +12 -12
  69. package/lib/rules/prefer-regex-literals.js +26 -27
  70. package/lib/rules/prefer-rest-params.js +5 -2
  71. package/lib/rules/radix.js +11 -11
  72. package/lib/rules/require-atomic-updates.js +2 -2
  73. package/lib/rules/require-unicode-regexp.js +63 -7
  74. package/lib/rules/symbol-description.js +7 -5
  75. package/lib/rules/utils/regular-expressions.js +42 -0
  76. package/lib/rules/valid-typeof.js +3 -3
  77. package/lib/rules/wrap-iife.js +1 -1
  78. package/lib/source-code/source-code.js +52 -1
  79. package/lib/source-code/token-store/index.js +1 -1
  80. package/lib/source-code/token-store/utils.js +14 -4
  81. package/messages/no-config-found.js +1 -1
  82. package/package.json +12 -7
  83. package/conf/eslint-all.js +0 -31
  84. package/conf/eslint-recommended.js +0 -76
@@ -61,6 +61,9 @@ module.exports = {
61
61
  fixable: "code"
62
62
  },
63
63
  create(context) {
64
+
65
+ const sourceCode = context.getSourceCode();
66
+
64
67
  return {
65
68
  CallExpression(node) {
66
69
  if (!(node.callee.type === "MemberExpression" && node.callee.object.type === "MemberExpression")) {
@@ -72,7 +75,7 @@ module.exports = {
72
75
  const isObject = hasLeftHandObject(node.callee.object);
73
76
 
74
77
  // check `Object` scope
75
- const scope = context.getScope();
78
+ const scope = sourceCode.getScope(node);
76
79
  const variable = astUtils.getVariableByName(scope, "Object");
77
80
 
78
81
  if (
@@ -85,7 +88,6 @@ module.exports = {
85
88
  node,
86
89
  messageId: "useHasOwn",
87
90
  fix(fixer) {
88
- const sourceCode = context.getSourceCode();
89
91
 
90
92
  if (sourceCode.getCommentsInside(node.callee).length > 0) {
91
93
  return null;
@@ -6,7 +6,7 @@
6
6
 
7
7
  "use strict";
8
8
 
9
- const { CALL, ReferenceTracker } = require("eslint-utils");
9
+ const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");
10
10
  const {
11
11
  isCommaToken,
12
12
  isOpeningParenToken,
@@ -265,8 +265,8 @@ module.exports = {
265
265
  const sourceCode = context.getSourceCode();
266
266
 
267
267
  return {
268
- Program() {
269
- const scope = context.getScope();
268
+ Program(node) {
269
+ const scope = sourceCode.getScope(node);
270
270
  const tracker = new ReferenceTracker(scope);
271
271
  const trackMap = {
272
272
  Object: {
@@ -275,22 +275,22 @@ module.exports = {
275
275
  };
276
276
 
277
277
  // Iterate all calls of `Object.assign` (only of the global variable `Object`).
278
- for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
278
+ for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
279
279
  if (
280
- node.arguments.length >= 1 &&
281
- node.arguments[0].type === "ObjectExpression" &&
282
- !hasArraySpread(node) &&
280
+ refNode.arguments.length >= 1 &&
281
+ refNode.arguments[0].type === "ObjectExpression" &&
282
+ !hasArraySpread(refNode) &&
283
283
  !(
284
- node.arguments.length > 1 &&
285
- hasArgumentsWithAccessors(node)
284
+ refNode.arguments.length > 1 &&
285
+ hasArgumentsWithAccessors(refNode)
286
286
  )
287
287
  ) {
288
- const messageId = node.arguments.length === 1
288
+ const messageId = refNode.arguments.length === 1
289
289
  ? "useLiteralMessage"
290
290
  : "useSpreadMessage";
291
- const fix = defineFixer(node, sourceCode);
291
+ const fix = defineFixer(refNode, sourceCode);
292
292
 
293
- context.report({ node, messageId, fix });
293
+ context.report({ node: refNode, messageId, fix });
294
294
  }
295
295
  }
296
296
  }
@@ -10,16 +10,15 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
- const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("eslint-utils");
14
- const { RegExpValidator, visitRegExpAST, RegExpParser } = require("regexpp");
13
+ const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("@eslint-community/eslint-utils");
14
+ const { RegExpValidator, visitRegExpAST, RegExpParser } = require("@eslint-community/regexpp");
15
15
  const { canTokensBeAdjacent } = require("./utils/ast-utils");
16
+ const { REGEXPP_LATEST_ECMA_VERSION } = require("./utils/regular-expressions");
16
17
 
17
18
  //------------------------------------------------------------------------------
18
19
  // Helpers
19
20
  //------------------------------------------------------------------------------
20
21
 
21
- const REGEXPP_LATEST_ECMA_VERSION = 2022;
22
-
23
22
  /**
24
23
  * Determines whether the given node is a string literal.
25
24
  * @param {ASTNode} node Node to check.
@@ -163,7 +162,7 @@ module.exports = {
163
162
  * @returns {boolean} True if the identifier is a reference to a global variable.
164
163
  */
165
164
  function isGlobalReference(node) {
166
- const scope = context.getScope();
165
+ const scope = sourceCode.getScope(node);
167
166
  const variable = findVariable(scope, node);
168
167
 
169
168
  return variable !== null && variable.scope.type === "global" && variable.defs.length === 0;
@@ -375,8 +374,8 @@ module.exports = {
375
374
  }
376
375
 
377
376
  return {
378
- Program() {
379
- const scope = context.getScope();
377
+ Program(node) {
378
+ const scope = sourceCode.getScope(node);
380
379
  const tracker = new ReferenceTracker(scope);
381
380
  const traceMap = {
382
381
  RegExp: {
@@ -385,16 +384,16 @@ module.exports = {
385
384
  }
386
385
  };
387
386
 
388
- for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
389
- if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(node)) {
390
- const regexNode = node.arguments[0];
387
+ for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
388
+ if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(refNode)) {
389
+ const regexNode = refNode.arguments[0];
391
390
 
392
- if (node.arguments.length === 2) {
391
+ if (refNode.arguments.length === 2) {
393
392
  const suggests = [];
394
393
 
395
- const argFlags = getStringValue(node.arguments[1]) || "";
394
+ const argFlags = getStringValue(refNode.arguments[1]) || "";
396
395
 
397
- if (canFixTo(node, regexNode.regex.pattern, argFlags)) {
396
+ if (canFixTo(refNode, regexNode.regex.pattern, argFlags)) {
398
397
  suggests.push({
399
398
  messageId: "replaceWithLiteralAndFlags",
400
399
  pattern: regexNode.regex.pattern,
@@ -407,7 +406,7 @@ module.exports = {
407
406
 
408
407
  if (
409
408
  !areFlagsEqual(mergedFlags, argFlags) &&
410
- canFixTo(node, regexNode.regex.pattern, mergedFlags)
409
+ canFixTo(refNode, regexNode.regex.pattern, mergedFlags)
411
410
  ) {
412
411
  suggests.push({
413
412
  messageId: "replaceWithIntendedLiteralAndFlags",
@@ -417,7 +416,7 @@ module.exports = {
417
416
  }
418
417
 
419
418
  context.report({
420
- node,
419
+ node: refNode,
421
420
  messageId: "unexpectedRedundantRegExpWithFlags",
422
421
  suggest: suggests.map(({ flags, pattern, messageId }) => ({
423
422
  messageId,
@@ -425,42 +424,42 @@ module.exports = {
425
424
  flags
426
425
  },
427
426
  fix(fixer) {
428
- return fixer.replaceText(node, getSafeOutput(node, `/${pattern}/${flags}`));
427
+ return fixer.replaceText(refNode, getSafeOutput(refNode, `/${pattern}/${flags}`));
429
428
  }
430
429
  }))
431
430
  });
432
431
  } else {
433
432
  const outputs = [];
434
433
 
435
- if (canFixTo(node, regexNode.regex.pattern, regexNode.regex.flags)) {
434
+ if (canFixTo(refNode, regexNode.regex.pattern, regexNode.regex.flags)) {
436
435
  outputs.push(sourceCode.getText(regexNode));
437
436
  }
438
437
 
439
438
 
440
439
  context.report({
441
- node,
440
+ node: refNode,
442
441
  messageId: "unexpectedRedundantRegExp",
443
442
  suggest: outputs.map(output => ({
444
443
  messageId: "replaceWithLiteral",
445
444
  fix(fixer) {
446
445
  return fixer.replaceText(
447
- node,
448
- getSafeOutput(node, output)
446
+ refNode,
447
+ getSafeOutput(refNode, output)
449
448
  );
450
449
  }
451
450
  }))
452
451
  });
453
452
  }
454
- } else if (hasOnlyStaticStringArguments(node)) {
455
- let regexContent = getStringValue(node.arguments[0]);
453
+ } else if (hasOnlyStaticStringArguments(refNode)) {
454
+ let regexContent = getStringValue(refNode.arguments[0]);
456
455
  let noFix = false;
457
456
  let flags;
458
457
 
459
- if (node.arguments[1]) {
460
- flags = getStringValue(node.arguments[1]);
458
+ if (refNode.arguments[1]) {
459
+ flags = getStringValue(refNode.arguments[1]);
461
460
  }
462
461
 
463
- if (!canFixTo(node, regexContent, flags)) {
462
+ if (!canFixTo(refNode, regexContent, flags)) {
464
463
  noFix = true;
465
464
  }
466
465
 
@@ -494,12 +493,12 @@ module.exports = {
494
493
  const newRegExpValue = `/${regexContent || "(?:)"}/${flags || ""}`;
495
494
 
496
495
  context.report({
497
- node,
496
+ node: refNode,
498
497
  messageId: "unexpectedRegExp",
499
498
  suggest: noFix ? [] : [{
500
499
  messageId: "replaceWithLiteral",
501
500
  fix(fixer) {
502
- return fixer.replaceText(node, getSafeOutput(node, newRegExpValue));
501
+ return fixer.replaceText(refNode, getSafeOutput(refNode, newRegExpValue));
503
502
  }
504
503
  }]
505
504
  });
@@ -79,6 +79,8 @@ module.exports = {
79
79
 
80
80
  create(context) {
81
81
 
82
+ const sourceCode = context.getSourceCode();
83
+
82
84
  /**
83
85
  * Reports a given reference.
84
86
  * @param {eslint-scope.Reference} reference A reference to report.
@@ -94,10 +96,11 @@ module.exports = {
94
96
 
95
97
  /**
96
98
  * Reports references of the implicit `arguments` variable if exist.
99
+ * @param {ASTNode} node The node representing the function.
97
100
  * @returns {void}
98
101
  */
99
- function checkForArguments() {
100
- const argumentsVar = getVariableOfArguments(context.getScope());
102
+ function checkForArguments(node) {
103
+ const argumentsVar = getVariableOfArguments(sourceCode.getScope(node));
101
104
 
102
105
  if (argumentsVar) {
103
106
  argumentsVar
@@ -104,6 +104,7 @@ module.exports = {
104
104
 
105
105
  create(context) {
106
106
  const mode = context.options[0] || MODE_ALWAYS;
107
+ const sourceCode = context.getSourceCode();
107
108
 
108
109
  /**
109
110
  * Checks the arguments of a given CallExpression node and reports it if it
@@ -131,7 +132,6 @@ module.exports = {
131
132
  {
132
133
  messageId: "addRadixParameter10",
133
134
  fix(fixer) {
134
- const sourceCode = context.getSourceCode();
135
135
  const tokens = sourceCode.getTokens(node);
136
136
  const lastToken = tokens[tokens.length - 1]; // Parenthesis.
137
137
  const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma.
@@ -162,18 +162,18 @@ module.exports = {
162
162
  }
163
163
 
164
164
  return {
165
- "Program:exit"() {
166
- const scope = context.getScope();
165
+ "Program:exit"(node) {
166
+ const scope = sourceCode.getScope(node);
167
167
  let variable;
168
168
 
169
169
  // Check `parseInt()`
170
170
  variable = astUtils.getVariableByName(scope, "parseInt");
171
171
  if (variable && !isShadowed(variable)) {
172
172
  variable.references.forEach(reference => {
173
- const node = reference.identifier;
173
+ const idNode = reference.identifier;
174
174
 
175
- if (astUtils.isCallee(node)) {
176
- checkArguments(node.parent);
175
+ if (astUtils.isCallee(idNode)) {
176
+ checkArguments(idNode.parent);
177
177
  }
178
178
  });
179
179
  }
@@ -182,12 +182,12 @@ module.exports = {
182
182
  variable = astUtils.getVariableByName(scope, "Number");
183
183
  if (variable && !isShadowed(variable)) {
184
184
  variable.references.forEach(reference => {
185
- const node = reference.identifier.parent;
186
- const maybeCallee = node.parent.type === "ChainExpression"
187
- ? node.parent
188
- : node;
185
+ const parentNode = reference.identifier.parent;
186
+ const maybeCallee = parentNode.parent.type === "ChainExpression"
187
+ ? parentNode.parent
188
+ : parentNode;
189
189
 
190
- if (isParseIntMethod(node) && astUtils.isCallee(maybeCallee)) {
190
+ if (isParseIntMethod(parentNode) && astUtils.isCallee(maybeCallee)) {
191
191
  checkArguments(maybeCallee.parent);
192
192
  }
193
193
  });
@@ -204,8 +204,8 @@ module.exports = {
204
204
  let stack = null;
205
205
 
206
206
  return {
207
- onCodePathStart(codePath) {
208
- const scope = context.getScope();
207
+ onCodePathStart(codePath, node) {
208
+ const scope = sourceCode.getScope(node);
209
209
  const shouldVerify =
210
210
  scope.type === "function" &&
211
211
  (scope.block.async || scope.block.generator);
@@ -14,7 +14,9 @@ const {
14
14
  CONSTRUCT,
15
15
  ReferenceTracker,
16
16
  getStringIfConstant
17
- } = require("eslint-utils");
17
+ } = require("@eslint-community/eslint-utils");
18
+ const astUtils = require("./utils/ast-utils.js");
19
+ const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
18
20
 
19
21
  //------------------------------------------------------------------------------
20
22
  // Rule Definition
@@ -31,7 +33,10 @@ module.exports = {
31
33
  url: "https://eslint.org/docs/rules/require-unicode-regexp"
32
34
  },
33
35
 
36
+ hasSuggestions: true,
37
+
34
38
  messages: {
39
+ addUFlag: "Add the 'u' flag.",
35
40
  requireUFlag: "Use the 'u' flag."
36
41
  },
37
42
 
@@ -39,28 +44,79 @@ module.exports = {
39
44
  },
40
45
 
41
46
  create(context) {
47
+
48
+ const sourceCode = context.getSourceCode();
49
+
42
50
  return {
43
51
  "Literal[regex]"(node) {
44
52
  const flags = node.regex.flags || "";
45
53
 
46
54
  if (!flags.includes("u")) {
47
- context.report({ node, messageId: "requireUFlag" });
55
+ context.report({
56
+ messageId: "requireUFlag",
57
+ node,
58
+ suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)
59
+ ? [
60
+ {
61
+ fix(fixer) {
62
+ return fixer.insertTextAfter(node, "u");
63
+ },
64
+ messageId: "addUFlag"
65
+ }
66
+ ]
67
+ : null
68
+ });
48
69
  }
49
70
  },
50
71
 
51
- Program() {
52
- const scope = context.getScope();
72
+ Program(node) {
73
+ const scope = sourceCode.getScope(node);
53
74
  const tracker = new ReferenceTracker(scope);
54
75
  const trackMap = {
55
76
  RegExp: { [CALL]: true, [CONSTRUCT]: true }
56
77
  };
57
78
 
58
- for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
59
- const flagsNode = node.arguments[1];
79
+ for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
80
+ const [patternNode, flagsNode] = refNode.arguments;
81
+ const pattern = getStringIfConstant(patternNode, scope);
60
82
  const flags = getStringIfConstant(flagsNode, scope);
61
83
 
62
84
  if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
63
- context.report({ node, messageId: "requireUFlag" });
85
+ context.report({
86
+ messageId: "requireUFlag",
87
+ node: refNode,
88
+ suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)
89
+ ? [
90
+ {
91
+ fix(fixer) {
92
+ if (flagsNode) {
93
+ if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
94
+ const flagsNodeText = sourceCode.getText(flagsNode);
95
+
96
+ return fixer.replaceText(flagsNode, [
97
+ flagsNodeText.slice(0, flagsNodeText.length - 1),
98
+ flagsNodeText.slice(flagsNodeText.length - 1)
99
+ ].join("u"));
100
+ }
101
+
102
+ // We intentionally don't suggest concatenating + "u" to non-literals
103
+ return null;
104
+ }
105
+
106
+ const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis
107
+
108
+ return fixer.insertTextAfter(
109
+ penultimateToken,
110
+ astUtils.isCommaToken(penultimateToken)
111
+ ? ' "u",'
112
+ : ', "u"'
113
+ );
114
+ },
115
+ messageId: "addUFlag"
116
+ }
117
+ ]
118
+ : null
119
+ });
64
120
  }
65
121
  }
66
122
  }
@@ -35,6 +35,8 @@ module.exports = {
35
35
 
36
36
  create(context) {
37
37
 
38
+ const sourceCode = context.getSourceCode();
39
+
38
40
  /**
39
41
  * Reports if node does not conform the rule in case rule is set to
40
42
  * report missing description
@@ -51,16 +53,16 @@ module.exports = {
51
53
  }
52
54
 
53
55
  return {
54
- "Program:exit"() {
55
- const scope = context.getScope();
56
+ "Program:exit"(node) {
57
+ const scope = sourceCode.getScope(node);
56
58
  const variable = astUtils.getVariableByName(scope, "Symbol");
57
59
 
58
60
  if (variable && variable.defs.length === 0) {
59
61
  variable.references.forEach(reference => {
60
- const node = reference.identifier;
62
+ const idNode = reference.identifier;
61
63
 
62
- if (astUtils.isCallee(node)) {
63
- checkArgument(node.parent);
64
+ if (astUtils.isCallee(idNode)) {
65
+ checkArgument(idNode.parent);
64
66
  }
65
67
  });
66
68
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview Common utils for regular expressions.
3
+ * @author Josh Goldberg
4
+ * @author Toru Nagashima
5
+ */
6
+
7
+ "use strict";
8
+
9
+ const { RegExpValidator } = require("@eslint-community/regexpp");
10
+
11
+ const REGEXPP_LATEST_ECMA_VERSION = 2022;
12
+
13
+ /**
14
+ * Checks if the given regular expression pattern would be valid with the `u` flag.
15
+ * @param {number} ecmaVersion ECMAScript version to parse in.
16
+ * @param {string} pattern The regular expression pattern to verify.
17
+ * @returns {boolean} `true` if the pattern would be valid with the `u` flag.
18
+ * `false` if the pattern would be invalid with the `u` flag or the configured
19
+ * ecmaVersion doesn't support the `u` flag.
20
+ */
21
+ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
22
+ if (ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
23
+ return false;
24
+ }
25
+
26
+ const validator = new RegExpValidator({
27
+ ecmaVersion: Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION)
28
+ });
29
+
30
+ try {
31
+ validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
32
+ } catch {
33
+ return false;
34
+ }
35
+
36
+ return true;
37
+ }
38
+
39
+ module.exports = {
40
+ isValidWithUnicodeFlag,
41
+ REGEXPP_LATEST_ECMA_VERSION
42
+ };
@@ -44,7 +44,7 @@ module.exports = {
44
44
 
45
45
  const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
46
46
  OPERATORS = new Set(["==", "===", "!=", "!=="]);
47
-
47
+ const sourceCode = context.getSourceCode();
48
48
  const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
49
49
 
50
50
  let globalScope;
@@ -77,8 +77,8 @@ module.exports = {
77
77
 
78
78
  return {
79
79
 
80
- Program() {
81
- globalScope = context.getScope();
80
+ Program(node) {
81
+ globalScope = sourceCode.getScope(node);
82
82
  },
83
83
 
84
84
  UnaryExpression(node) {
@@ -10,7 +10,7 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
- const eslintUtils = require("eslint-utils");
13
+ const eslintUtils = require("@eslint-community/eslint-utils");
14
14
 
15
15
  //----------------------------------------------------------------------
16
16
  // Helpers
@@ -9,7 +9,7 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const
12
- { isCommentToken } = require("eslint-utils"),
12
+ { isCommentToken } = require("@eslint-community/eslint-utils"),
13
13
  TokenStore = require("./token-store"),
14
14
  astUtils = require("../shared/ast-utils"),
15
15
  Traverser = require("../shared/traverser");
@@ -143,6 +143,8 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
143
143
  // Public Interface
144
144
  //------------------------------------------------------------------------------
145
145
 
146
+ const caches = Symbol("caches");
147
+
146
148
  /**
147
149
  * Represents parsed source code.
148
150
  */
@@ -175,6 +177,13 @@ class SourceCode extends TokenStore {
175
177
  validate(ast);
176
178
  super(ast.tokens, ast.comments);
177
179
 
180
+ /**
181
+ * General purpose caching for the class.
182
+ */
183
+ this[caches] = new Map([
184
+ ["scopes", new WeakMap()]
185
+ ]);
186
+
178
187
  /**
179
188
  * The flag to indicate that the source code has Unicode BOM.
180
189
  * @type {boolean}
@@ -588,6 +597,48 @@ class SourceCode extends TokenStore {
588
597
 
589
598
  return positionIndex;
590
599
  }
600
+
601
+ /**
602
+ * Gets the scope for the given node
603
+ * @param {ASTNode} currentNode The node to get the scope of
604
+ * @returns {eslint-scope.Scope} The scope information for this node
605
+ * @throws {TypeError} If the `currentNode` argument is missing.
606
+ */
607
+ getScope(currentNode) {
608
+
609
+ if (!currentNode) {
610
+ throw new TypeError("Missing required argument: node.");
611
+ }
612
+
613
+ // check cache first
614
+ const cache = this[caches].get("scopes");
615
+ const cachedScope = cache.get(currentNode);
616
+
617
+ if (cachedScope) {
618
+ return cachedScope;
619
+ }
620
+
621
+ // On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
622
+ const inner = currentNode.type !== "Program";
623
+
624
+ for (let node = currentNode; node; node = node.parent) {
625
+ const scope = this.scopeManager.acquire(node, inner);
626
+
627
+ if (scope) {
628
+ if (scope.type === "function-expression-name") {
629
+ cache.set(currentNode, scope.childScopes[0]);
630
+ return scope.childScopes[0];
631
+ }
632
+
633
+ cache.set(currentNode, scope);
634
+ return scope;
635
+ }
636
+ }
637
+
638
+ cache.set(currentNode, this.scopeManager.scopes[0]);
639
+ return this.scopeManager.scopes[0];
640
+ }
641
+
591
642
  }
592
643
 
593
644
  module.exports = SourceCode;
@@ -9,7 +9,7 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const assert = require("assert");
12
- const { isCommentToken } = require("eslint-utils");
12
+ const { isCommentToken } = require("@eslint-community/eslint-utils");
13
13
  const cursors = require("./cursors");
14
14
  const ForwardTokenCursor = require("./forward-token-cursor");
15
15
  const PaddedTokenCursor = require("./padded-token-cursor");
@@ -49,13 +49,18 @@ exports.getFirstIndex = function getFirstIndex(tokens, indexMap, startLoc) {
49
49
  }
50
50
  if ((startLoc - 1) in indexMap) {
51
51
  const index = indexMap[startLoc - 1];
52
- const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
52
+ const token = tokens[index];
53
+
54
+ // If the mapped index is out of bounds, the returned cursor index will point after the end of the tokens array.
55
+ if (!token) {
56
+ return tokens.length;
57
+ }
53
58
 
54
59
  /*
55
60
  * For the map of "comment's location -> token's index", it points the next token of a comment.
56
61
  * In that case, +1 is unnecessary.
57
62
  */
58
- if (token && token.range[0] >= startLoc) {
63
+ if (token.range[0] >= startLoc) {
59
64
  return index;
60
65
  }
61
66
  return index + 1;
@@ -77,13 +82,18 @@ exports.getLastIndex = function getLastIndex(tokens, indexMap, endLoc) {
77
82
  }
78
83
  if ((endLoc - 1) in indexMap) {
79
84
  const index = indexMap[endLoc - 1];
80
- const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
85
+ const token = tokens[index];
86
+
87
+ // If the mapped index is out of bounds, the returned cursor index will point before the end of the tokens array.
88
+ if (!token) {
89
+ return tokens.length - 1;
90
+ }
81
91
 
82
92
  /*
83
93
  * For the map of "comment's location -> token's index", it points the next token of a comment.
84
94
  * In that case, -1 is necessary.
85
95
  */
86
- if (token && token.range[1] > endLoc) {
96
+ if (token.range[1] > endLoc) {
87
97
  return index - 1;
88
98
  }
89
99
  return index;