eslint-plugin-unicorn 70.0.0 → 71.1.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 (91) hide show
  1. package/package.json +26 -25
  2. package/readme.md +5 -1
  3. package/rules/ast/call-or-new-expression.js +2 -8
  4. package/rules/ast/is-member-expression.js +2 -8
  5. package/rules/comment-content.js +4 -4
  6. package/rules/consistent-boolean-name.js +275 -29
  7. package/rules/consistent-compound-words.js +1 -2
  8. package/rules/consistent-json-file-read.js +2 -5
  9. package/rules/dom-node-dataset.js +3 -7
  10. package/rules/escape-case.js +4 -4
  11. package/rules/expiring-todo-comments.js +2 -2
  12. package/rules/explicit-length-check.js +3 -3
  13. package/rules/filename-case.js +4 -4
  14. package/rules/fix/remove-argument.js +34 -8
  15. package/rules/fix/remove-statement.js +1 -3
  16. package/rules/id-match.js +2 -2
  17. package/rules/isolated-functions.js +47 -7
  18. package/rules/name-replacements.js +1 -2
  19. package/rules/no-anonymous-default-export.js +1 -3
  20. package/rules/no-array-callback-reference.js +2 -10
  21. package/rules/no-chained-comparison.js +1 -1
  22. package/rules/no-computed-property-existence-check.js +1 -4
  23. package/rules/no-declarations-before-early-exit.js +6 -5
  24. package/rules/no-error-property-assignment.js +3 -3
  25. package/rules/no-for-loop.js +4 -4
  26. package/rules/no-keyword-prefix.js +2 -2
  27. package/rules/no-mismatched-map-key.js +13 -20
  28. package/rules/no-named-default.js +2 -5
  29. package/rules/no-negated-array-predicate.js +4 -11
  30. package/rules/no-negated-comparison.js +7 -10
  31. package/rules/no-negation-in-equality-check.js +2 -5
  32. package/rules/no-process-exit.js +6 -6
  33. package/rules/no-static-only-class.js +2 -8
  34. package/rules/no-thenable.js +9 -15
  35. package/rules/no-undeclared-class-members.js +1 -5
  36. package/rules/no-unnecessary-array-flat-map.js +6 -5
  37. package/rules/no-unnecessary-global-this.js +5 -9
  38. package/rules/no-unused-properties.js +0 -1
  39. package/rules/no-useless-coercion.js +1 -2
  40. package/rules/no-useless-undefined.js +2 -2
  41. package/rules/prefer-array-find.js +2 -5
  42. package/rules/prefer-bigint-literals.js +1 -0
  43. package/rules/prefer-boolean-return.js +20 -3
  44. package/rules/prefer-default-parameters.js +2 -2
  45. package/rules/prefer-else-if.js +5 -9
  46. package/rules/prefer-has-check.js +6 -3
  47. package/rules/prefer-https.js +3 -3
  48. package/rules/prefer-identifier-import-export-specifiers.js +1 -3
  49. package/rules/prefer-minimal-ternary.js +4 -4
  50. package/rules/prefer-object-define-properties.js +1 -3
  51. package/rules/prefer-observer-apis.js +2 -5
  52. package/rules/prefer-private-class-fields.js +1 -3
  53. package/rules/prefer-simple-sort-comparator.js +6 -6
  54. package/rules/prefer-single-object-destructuring.js +2 -5
  55. package/rules/prefer-single-replace.js +2 -2
  56. package/rules/prefer-spread.js +22 -32
  57. package/rules/prefer-string-pad-start-end.js +1 -6
  58. package/rules/prefer-string-repeat.js +16 -9
  59. package/rules/prefer-string-replace-all.js +1 -1
  60. package/rules/prefer-ternary.js +5 -5
  61. package/rules/prefer-toggle-attribute.js +3 -3
  62. package/rules/prefer-top-level-await.js +6 -1
  63. package/rules/prefer-uint8array-base64.js +3 -1
  64. package/rules/require-proxy-trap-boolean-return.js +2 -2
  65. package/rules/rule/to-eslint-create.js +1 -2
  66. package/rules/rule/to-eslint-listener.js +1 -3
  67. package/rules/rule/to-eslint-problem.js +1 -3
  68. package/rules/rule/to-eslint-rule-fixer.js +1 -3
  69. package/rules/rule/to-eslint-rule.js +1 -3
  70. package/rules/rule/to-eslint-rules.js +1 -3
  71. package/rules/rule/unicorn-context.js +1 -3
  72. package/rules/rule/unicorn-listeners.js +1 -3
  73. package/rules/shared/no-array-mutate-rule.js +1 -3
  74. package/rules/shared/regexp-escape.js +4 -4
  75. package/rules/utils/can-tokens-be-adjacent.js +95 -0
  76. package/rules/utils/escape-string.js +2 -8
  77. package/rules/utils/get-available-variable-name.js +2 -13
  78. package/rules/utils/get-precedence.js +112 -0
  79. package/rules/utils/get-token-store.js +1 -3
  80. package/rules/utils/global-reference-tracker.js +3 -6
  81. package/rules/utils/index.js +4 -0
  82. package/rules/utils/is-boolean.js +90 -32
  83. package/rules/utils/is-identifier-name.js +5 -0
  84. package/rules/utils/is-promise-type.js +1 -3
  85. package/rules/utils/is-react-hook-name.js +5 -0
  86. package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
  87. package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
  88. package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
  89. package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
  90. package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
  91. package/rules/utils/to-location.js +1 -3
@@ -18,6 +18,7 @@ import {
18
18
  isTypeParameterType,
19
19
  hasUnparenthesizedOptionalChainElement,
20
20
  isTypeScriptExpressionWrapper,
21
+ isDefaultLibrarySymbol,
21
22
  } from './utils/index.js';
22
23
  import {removeMethodCall} from './fix/index.js';
23
24
  import {
@@ -155,8 +156,8 @@ function fixConcat(node, context, fixableArguments) {
155
156
  const array = node.callee.object;
156
157
  const concatCallArguments = node.arguments;
157
158
  const arrayParenthesizedRange = getParenthesizedRange(array, context);
158
- const arrayIsArrayLiteral = isArrayLiteral(array);
159
- const arrayHasTrailingComma = arrayIsArrayLiteral && isArrayLiteralHasTrailingComma(array, sourceCode);
159
+ const isArrayIsArrayLiteral = isArrayLiteral(array);
160
+ const arrayHasTrailingComma = isArrayIsArrayLiteral && isArrayLiteralHasTrailingComma(array, sourceCode);
160
161
 
161
162
  const getArrayLiteralElementsText = (node, keepTrailingComma) => {
162
163
  if (
@@ -204,7 +205,7 @@ function fixConcat(node, context, fixableArguments) {
204
205
  return '';
205
206
  }
206
207
 
207
- if (arrayIsArrayLiteral) {
208
+ if (isArrayIsArrayLiteral) {
208
209
  if (!isEmptyArrayExpression(array)) {
209
210
  text = ` ${text}`;
210
211
 
@@ -243,7 +244,7 @@ function fixConcat(node, context, fixableArguments) {
243
244
  return function * (fixer) {
244
245
  // Fixed code always starts with `[`
245
246
  if (
246
- !arrayIsArrayLiteral
247
+ !isArrayIsArrayLiteral
247
248
  && needsSemicolon(sourceCode.getTokenBefore(node), context, '[')
248
249
  ) {
249
250
  yield fixer.insertTextBefore(node, ';');
@@ -257,7 +258,7 @@ function fixConcat(node, context, fixableArguments) {
257
258
 
258
259
  const text = getFixedText();
259
260
 
260
- if (arrayIsArrayLiteral) {
261
+ if (isArrayIsArrayLiteral) {
261
262
  const closingBracketToken = sourceCode.getLastToken(array);
262
263
  yield fixer.insertTextBefore(closingBracketToken, text);
263
264
  } else {
@@ -389,9 +390,6 @@ function isGlobalMemberExpression(node, objectName, propertyName, context) {
389
390
  return staticValue?.value === propertyName;
390
391
  }
391
392
 
392
- const isDefaultLibrarySymbol = (symbol, program) =>
393
- symbol?.declarations?.some(declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile())) ?? false;
394
-
395
393
  function isBufferModuleImport(definition) {
396
394
  return (
397
395
  definition?.type === 'ImportBinding'
@@ -1339,11 +1337,11 @@ const create = context => {
1339
1337
  };
1340
1338
 
1341
1339
  const fixableArguments = getConcatFixableArguments(node.arguments, scope, context);
1342
- const receiverSafeToSpread = !isArrayConstructorWithOneArgument(object, context);
1340
+ const isReceiverSafeToSpread = !isArrayConstructorWithOneArgument(object, context);
1343
1341
 
1344
1342
  if (fixableArguments.length > 0 || node.arguments.length === 0) {
1345
1343
  if (
1346
- receiverSafeToSpread
1344
+ isReceiverSafeToSpread
1347
1345
  && !hasCommentsOutsideRanges(node, getConcatPreservedRanges(node, fixableArguments.length))
1348
1346
  ) {
1349
1347
  problem.fix = fixConcat(node, context, fixableArguments);
@@ -1357,7 +1355,7 @@ const create = context => {
1357
1355
  return problem;
1358
1356
  }
1359
1357
 
1360
- if (!receiverSafeToSpread) {
1358
+ if (!isReceiverSafeToSpread) {
1361
1359
  return problem;
1362
1360
  }
1363
1361
 
@@ -1422,17 +1420,13 @@ const create = context => {
1422
1420
 
1423
1421
  // `array.slice()`
1424
1422
  context.on('CallExpression', node => {
1425
- if (!(
1426
- isMethodCall(node, {
1427
- method: 'slice',
1428
- minimumArguments: 0,
1429
- maximumArguments: 1,
1430
- optionalCall: false,
1431
- optionalMember: false,
1432
- })
1433
- && !isArrayLiteral(node.callee.object)
1434
- && !hasUnparenthesizedOptionalChainElement(node.callee.object, context)
1435
- )) {
1423
+ if (!isMethodCall(node, {
1424
+ method: 'slice',
1425
+ minimumArguments: 0,
1426
+ maximumArguments: 1,
1427
+ optionalCall: false,
1428
+ optionalMember: false,
1429
+ }) || isArrayLiteral(node.callee.object) || hasUnparenthesizedOptionalChainElement(node.callee.object, context)) {
1436
1430
  return;
1437
1431
  }
1438
1432
 
@@ -1465,16 +1459,12 @@ const create = context => {
1465
1459
 
1466
1460
  // `array.toSpliced()`
1467
1461
  context.on('CallExpression', node => {
1468
- if (!(
1469
- isMethodCall(node, {
1470
- method: 'toSpliced',
1471
- argumentsLength: 0,
1472
- optionalCall: false,
1473
- optionalMember: false,
1474
- })
1475
- && node.callee.object.type !== 'ArrayExpression'
1476
- && !hasUnparenthesizedOptionalChainElement(node.callee.object, context)
1477
- )) {
1462
+ if (!isMethodCall(node, {
1463
+ method: 'toSpliced',
1464
+ argumentsLength: 0,
1465
+ optionalCall: false,
1466
+ optionalMember: false,
1467
+ }) || node.callee.object.type === 'ArrayExpression' || hasUnparenthesizedOptionalChainElement(node.callee.object, context)) {
1478
1468
  return;
1479
1469
  }
1480
1470
 
@@ -1,5 +1,5 @@
1
1
  import {findVariable, getStaticValue} from '@eslint-community/eslint-utils';
2
- import {isMethodCall} from './ast/index.js';
2
+ import {isMethodCall, isNumericLiteral} from './ast/index.js';
3
3
  import {
4
4
  getParenthesizedText,
5
5
  isSameReference,
@@ -78,11 +78,6 @@ const hasCommentsInside = (node, context) => context.sourceCode.getCommentsInsid
78
78
 
79
79
  const isSimpleTarget = node => node.type === 'Identifier';
80
80
 
81
- const isNumericLiteral = node => (
82
- node.type === 'Literal'
83
- && typeof node.value === 'number'
84
- );
85
-
86
81
  const isZeroLiteral = node => (
87
82
  isNumericLiteral(node)
88
83
  && node.value === 0
@@ -1,4 +1,3 @@
1
- import jsesc from 'jsesc';
2
1
  import {isStringLiteral, isDirective} from './ast/index.js';
3
2
  import {fixSpaceAroundKeyword} from './fix/index.js';
4
3
  import isJestInlineSnapshot from './shared/is-jest-inline-snapshot.js';
@@ -10,15 +9,23 @@ const messages = {
10
9
 
11
10
  const singleWhitespace = /^\s$/u;
12
11
 
12
+ const namedEscapes = new Map([
13
+ ['\t', String.raw`\t`],
14
+ ['\n', String.raw`\n`],
15
+ ['\r', String.raw`\r`],
16
+ ['\f', String.raw`\f`],
17
+ ]);
18
+
19
+ // Escape a single whitespace character into a readable single-quoted string literal.
13
20
  function quoteWhitespace(character) {
14
- return jsesc(character, {
15
- quotes: 'single',
16
- wrap: true,
17
- es6: true,
18
- minimal: false,
19
- lowercaseHex: false,
20
- json: true,
21
- });
21
+ if (character === ' ') {
22
+ return '\' \'';
23
+ }
24
+
25
+ const escape = namedEscapes.get(character)
26
+ ?? String.raw`\u{${character.codePointAt(0).toString(16).toUpperCase()}}`;
27
+
28
+ return `'${escape}'`;
22
29
  }
23
30
 
24
31
  function getRepeatedWhitespace(value, minimumRepetitions) {
@@ -40,7 +40,7 @@ function hasSafeGlobalStringReplacementFlags(flags) {
40
40
  return true;
41
41
  }
42
42
 
43
- const getUnicodeEscape = codePoint => String.raw`\u{${codePoint.toString(16)}}`;
43
+ const getUnicodeEscape = codePoint => String.raw`\u{${codePoint.toString(16).toUpperCase()}}`;
44
44
 
45
45
  function getValueReplacement(node) {
46
46
  const {kind, codePoint, raw} = node;
@@ -1,4 +1,5 @@
1
1
  import {hasSideEffect, findVariable} from '@eslint-community/eslint-utils';
2
+ import {isBooleanLiteral} from './ast/index.js';
2
3
  import {
3
4
  needsSemicolon,
4
5
  isSameReference,
@@ -13,7 +14,6 @@ const messageId = 'prefer-ternary';
13
14
  const suggestionMessageId = 'prefer-ternary/suggestion';
14
15
 
15
16
  const isTernary = node => node?.type === 'ConditionalExpression';
16
- const isBooleanLiteral = node => node?.type === 'Literal' && typeof node.value === 'boolean';
17
17
 
18
18
  function getNodeBody(node) {
19
19
  /* c8 ignore next 3 */
@@ -57,7 +57,7 @@ const isMergeableAssignmentExpression = (consequent, alternate) =>
57
57
 
58
58
  /** @param {import('eslint').Rule.RuleContext} context */
59
59
  const create = context => {
60
- const onlySingleLine = context.options[0] === 'only-single-line';
60
+ const isOnlySingleLine = context.options[0] === 'only-single-line';
61
61
  const {sourceCode} = context;
62
62
 
63
63
  const getText = node => {
@@ -134,7 +134,7 @@ const create = context => {
134
134
  }
135
135
 
136
136
  if (
137
- onlySingleLine
137
+ isOnlySingleLine
138
138
  && [node.test, right].some(n => !isSingleLineNode(n, context))
139
139
  ) {
140
140
  return;
@@ -161,7 +161,7 @@ const create = context => {
161
161
  }
162
162
 
163
163
  if (
164
- onlySingleLine
164
+ isOnlySingleLine
165
165
  && !isSingleLineNode(declarator.init, context)
166
166
  ) {
167
167
  return;
@@ -245,7 +245,7 @@ const create = context => {
245
245
  const alternate = getNodeBody(node.alternate);
246
246
 
247
247
  if (
248
- onlySingleLine
248
+ isOnlySingleLine
249
249
  && [consequent, alternate, node.test].some(node => !isSingleLineNode(node, context))
250
250
  ) {
251
251
  return;
@@ -224,15 +224,15 @@ const create = context => {
224
224
  }
225
225
 
226
226
  const setCall = consequentCall.method === 'setAttribute' ? consequentCall : alternateCall;
227
- const setWhenTrue = consequentCall === setCall;
227
+ const isSetWhenTrue = consequentCall === setCall;
228
228
  const hasAttributeCondition = getHasAttributeCondition(node.test, setCall, context);
229
229
 
230
230
  if (hasAttributeCondition?.isKnownNonDom) {
231
231
  return;
232
232
  }
233
233
 
234
- const shouldToggleWithoutForce = Boolean(hasAttributeCondition) && setWhenTrue === hasAttributeCondition.isNegative;
235
- const conditionText = shouldToggleWithoutForce ? '' : getConditionText(node.test, context, !setWhenTrue);
234
+ const shouldToggleWithoutForce = Boolean(hasAttributeCondition) && isSetWhenTrue === hasAttributeCondition.isNegative;
235
+ const conditionText = shouldToggleWithoutForce ? '' : getConditionText(node.test, context, !isSetWhenTrue);
236
236
  const hasSafeAttributeValue = setCall.hasEmptyAttributeValue || getStaticStringValue(setCall.attributeValue) !== undefined;
237
237
  const shouldReportOnly = (Boolean(conditionText) && setCall.isOptional) || !hasSafeAttributeValue;
238
238
 
@@ -205,7 +205,12 @@ const isInPromiseMethods = node => {
205
205
 
206
206
  /** @param {import('eslint').Rule.RuleContext} context */
207
207
  function create(context) {
208
- if (context.filename.toLowerCase().endsWith('.cjs')) {
208
+ // Use the real file path so processors or code blocks cannot hide file-level opt-outs.
209
+ const filename = context.physicalFilename.toLowerCase();
210
+ if (
211
+ filename.endsWith('.cjs')
212
+ || filename.endsWith('.svelte')
213
+ ) {
209
214
  return;
210
215
  }
211
216
 
@@ -152,7 +152,9 @@ const config = {
152
152
  type: 'suggestion',
153
153
  docs: {
154
154
  description: 'Prefer `Uint8Array#toBase64()` and `Uint8Array.fromBase64()` over `atob()`, `btoa()`, and `Buffer` base64 conversions.',
155
- recommended: 'unopinionated',
155
+ // eslint-disable-next-line no-warning-comments
156
+ // TODO: Enable in the `recommended` and `unopinionated` configs when targeting Node.js 26.
157
+ recommended: false,
156
158
  },
157
159
  hasSuggestions: true,
158
160
  messages,
@@ -277,8 +277,8 @@ const create = context => {
277
277
  return;
278
278
  }
279
279
 
280
- const allUnreachable = [...currentSegments()].every(segment => !segment.reachable);
281
- functionBodyAlwaysExits.set(body.parent, allUnreachable);
280
+ const isAllUnreachable = [...currentSegments()].every(segment => !segment.reachable);
281
+ functionBodyAlwaysExits.set(body.parent, isAllUnreachable);
282
282
  });
283
283
 
284
284
  function * checkCallOrNewExpression(node) {
@@ -26,7 +26,7 @@ Convert Unicorn style of `create` to ESLint style
26
26
  @param {UnicornCreate} unicornCreate
27
27
  @returns {EslintCreate}
28
28
  */
29
- function toEslintCreate(unicornCreate) {
29
+ export default function toEslintCreate(unicornCreate) {
30
30
  if (wrappedFunctions.has(unicornCreate)) {
31
31
  return unicornCreate;
32
32
  }
@@ -45,5 +45,4 @@ function toEslintCreate(unicornCreate) {
45
45
  };
46
46
  }
47
47
 
48
- export default toEslintCreate;
49
48
  export {markFunctionWrapped};
@@ -19,7 +19,7 @@ import toEslintProblem from './to-eslint-problem.js';
19
19
  @param {UnicornRuleListen} listener
20
20
  @returns {Listener}
21
21
  */
22
- function toEslintListener(context, listener) {
22
+ export default function toEslintListener(context, listener) {
23
23
  // Listener arguments can be `codePath, node` or `node`
24
24
 
25
25
  /**
@@ -38,5 +38,3 @@ function toEslintListener(context, listener) {
38
38
  }
39
39
  };
40
40
  }
41
-
42
- export default toEslintListener;
@@ -14,7 +14,7 @@ import toEslintFixer from './to-eslint-rule-fixer.js';
14
14
  @param {UnicornProblem} unicornProblem
15
15
  @returns {EslintProblem}
16
16
  */
17
- function toEslintProblem(unicornProblem) {
17
+ export default function toEslintProblem(unicornProblem) {
18
18
  const eslintProblem = {...unicornProblem};
19
19
 
20
20
  if (unicornProblem.fix) {
@@ -34,5 +34,3 @@ function toEslintProblem(unicornProblem) {
34
34
 
35
35
  return eslintProblem;
36
36
  }
37
-
38
- export default toEslintProblem;
@@ -26,7 +26,7 @@ Convert Unicorn style fix function to ESLint style fix function
26
26
  @param {UnicornRuleFixer} fix
27
27
  @returns {ESLint.Rule.RuleFixer}
28
28
  */
29
- function toEslintRuleFixer(fix) {
29
+ export default function toEslintRuleFixer(fix) {
30
30
  /** @param {UnicornReportFixer} fixer */
31
31
  return fixer => {
32
32
  const unicornReport = fix(fixer, fixOptions);
@@ -45,5 +45,3 @@ function toEslintRuleFixer(fix) {
45
45
  }
46
46
  };
47
47
  }
48
-
49
- export default toEslintRuleFixer;
@@ -19,7 +19,7 @@ Convert Unicorn rule to ESLint rule
19
19
  @param {UnicornRule} unicornRule
20
20
  @returns {ESLint.Rule.RuleModule}
21
21
  */
22
- function toEslintRule(ruleId, unicornRule) {
22
+ export default function toEslintRule(ruleId, unicornRule) {
23
23
  return {
24
24
  meta: {
25
25
  // If there is are, options add `[]` so ESLint can validate that no data is passed to the rule.
@@ -34,5 +34,3 @@ function toEslintRule(ruleId, unicornRule) {
34
34
  create: toEslintCreate(unicornRule.create),
35
35
  };
36
36
  }
37
-
38
- export default toEslintRule;
@@ -1,11 +1,9 @@
1
1
  import toEslintRule from './to-eslint-rule.js';
2
2
 
3
- function toEslintRules(rules) {
3
+ export default function toEslintRules(rules) {
4
4
  return Object.fromEntries(Object.entries(rules).map(([ruleId, rule]) => [
5
5
  ruleId,
6
6
  toEslintRule(ruleId, rule),
7
7
  ]));
8
8
  }
9
9
 
10
- export default toEslintRules;
11
-
@@ -18,7 +18,7 @@ Create a better `Context` object with `on` and `onExit` method to add listeners
18
18
  @param {UnicornListeners} listeners
19
19
  @returns {UnicornContext}
20
20
  */
21
- function createUnicornContext(eslintContext, listeners) {
21
+ export default function createUnicornContext(eslintContext, listeners) {
22
22
  /** @type {UnicornContext} */
23
23
  const context = new Proxy(eslintContext, {
24
24
  get(target, property, receiver) {
@@ -32,5 +32,3 @@ function createUnicornContext(eslintContext, listeners) {
32
32
 
33
33
  return context;
34
34
  }
35
-
36
- export default createUnicornContext;
@@ -4,7 +4,7 @@ import toEslintListener from './to-eslint-listener.js';
4
4
  @import {EslintListers, ListenerType, Listener} from './to-eslint-create.js'
5
5
  */
6
6
 
7
- class UnicornListeners {
7
+ export default class UnicornListeners {
8
8
  #context;
9
9
  #listeners = new Map();
10
10
 
@@ -61,5 +61,3 @@ class UnicornListeners {
61
61
  return eslintListeners;
62
62
  }
63
63
  }
64
-
65
- export default UnicornListeners;
@@ -50,7 +50,7 @@ const schema = [
50
50
  },
51
51
  ];
52
52
 
53
- function noArrayMutateRule(methodName) {
53
+ export default function noArrayMutateRule(methodName) {
54
54
  const {
55
55
  replacement,
56
56
  predicate,
@@ -159,5 +159,3 @@ function noArrayMutateRule(methodName) {
159
159
 
160
160
  return config;
161
161
  }
162
-
163
- export default noArrayMutateRule;
@@ -19,12 +19,12 @@ const getCharacterClassCharacters = pattern => {
19
19
 
20
20
  for (let index = 0; index < characterClass.length; index++) {
21
21
  let character = characterClass[index];
22
- let escaped = false;
22
+ let isEscaped = false;
23
23
 
24
24
  if (character === '\\') {
25
25
  index++;
26
26
  character = characterClass[index];
27
- escaped = true;
27
+ isEscaped = true;
28
28
 
29
29
  if (!character) {
30
30
  return;
@@ -33,7 +33,7 @@ const getCharacterClassCharacters = pattern => {
33
33
 
34
34
  if (
35
35
  character === '-'
36
- && !escaped
36
+ && !isEscaped
37
37
  && index !== 0
38
38
  && index !== characterClass.length - 1
39
39
  ) {
@@ -42,7 +42,7 @@ const getCharacterClassCharacters = pattern => {
42
42
 
43
43
  if (
44
44
  character === ']'
45
- && !escaped
45
+ && !isEscaped
46
46
  && index !== 0
47
47
  ) {
48
48
  return;
@@ -0,0 +1,95 @@
1
+ /**
2
+ @import * as ESLint from 'eslint';
3
+ */
4
+
5
+ const WORD_CHARACTER = /[\w$]/u;
6
+ const DECIMAL_INTEGER_LITERAL = /^\d(?:_?\d)*$/u;
7
+ const TRAILING_DECIMAL_INTEGER_LITERAL = /(?:^|[^\w$])\d(?:_?\d)*$/u;
8
+ const TRAILING_DIGIT_DOT = /\d\.$/u;
9
+
10
+ /**
11
+ @param {ESLint.AST.Token | string} value
12
+ @returns {string}
13
+ */
14
+ function getText(value) {
15
+ return typeof value === 'string' ? value : value.value;
16
+ }
17
+
18
+ /**
19
+ @param {ESLint.AST.Token | string} value
20
+ @returns {boolean}
21
+ */
22
+ function isLineCommentOrShebang(value) {
23
+ return typeof value === 'object' && ['Line', 'Shebang', 'Hashbang'].includes(value.type);
24
+ }
25
+
26
+ /**
27
+ @param {ESLint.AST.Token | string} value
28
+ @returns {boolean}
29
+ */
30
+ function isComment(value) {
31
+ return typeof value === 'object' && ['Line', 'Block'].includes(value.type);
32
+ }
33
+
34
+ /**
35
+ @param {ESLint.AST.Token | string} value
36
+ @returns {boolean}
37
+ */
38
+ function isUnsafeNumericDotLeft(value) {
39
+ return typeof value === 'object' ? value.type === 'Numeric' && DECIMAL_INTEGER_LITERAL.test(value.value) : TRAILING_DECIMAL_INTEGER_LITERAL.test(value);
40
+ }
41
+
42
+ /**
43
+ Checks whether `left` and `right` can be placed directly next to each other without the last character of `left` and the first character of `right` merging into a token that changes the meaning of the code (for example two identifiers becoming one, `+` and `+` becoming `++`, or `2` and `.2` becoming `2.2`).
44
+
45
+ This is a lightweight, character-boundary check, not a full tokenizer, inspired by ESLint's own (more thorough) `canTokensBeAdjacent`: https://github.com/eslint/eslint/blob/b23015955c8d6e6516076190730f538c86927f26/lib/rules/utils/ast-utils.js#L2522-L2529
46
+ It only guards against the adjacency hazards that come up when building fixer replacement text in this codebase: identifiers/keywords/numbers merging, `+`/`-` doubling into `++`/`--`, a numeric literal absorbing a following decimal point, and a `/` being swallowed into a following comment. It intentionally doesn't try to detect hazards that require a real tokenizer, like an already-open `//` comment inside `left`, or malformed token text.
47
+
48
+ @param {ESLint.AST.Token | string} left - The left token or text.
49
+ @param {ESLint.AST.Token | string} right - The right token or text.
50
+ @returns {boolean} `false` if a space is needed between `left` and `right` to keep them from merging. `true` if they can safely be adjacent.
51
+ */
52
+ export default function canTokensBeAdjacent(left, right) {
53
+ if (isLineCommentOrShebang(left)) {
54
+ return false;
55
+ }
56
+
57
+ const leftText = getText(left);
58
+
59
+ if (leftText === '') {
60
+ return true;
61
+ }
62
+
63
+ const rightText = getText(right);
64
+ const lastCharacter = leftText.at(-1);
65
+
66
+ if (lastCharacter === '/' && isComment(right)) {
67
+ return false;
68
+ }
69
+
70
+ if (rightText === '') {
71
+ return true;
72
+ }
73
+
74
+ const firstCharacter = rightText[0];
75
+
76
+ if (WORD_CHARACTER.test(lastCharacter) && WORD_CHARACTER.test(firstCharacter)) {
77
+ return false;
78
+ }
79
+
80
+ if (
81
+ (lastCharacter === '+' && firstCharacter === '+')
82
+ || (lastCharacter === '-' && firstCharacter === '-')
83
+ ) {
84
+ return false;
85
+ }
86
+
87
+ if (
88
+ (isUnsafeNumericDotLeft(left) && firstCharacter === '.')
89
+ || (TRAILING_DIGIT_DOT.test(leftText) && WORD_CHARACTER.test(firstCharacter))
90
+ ) {
91
+ return false;
92
+ }
93
+
94
+ return !(lastCharacter === '/' && (firstCharacter === '/' || firstCharacter === '*'));
95
+ }
@@ -1,4 +1,4 @@
1
- import jsesc from 'jsesc';
1
+ import quoteJsString from 'quote-js-string';
2
2
 
3
3
  /**
4
4
  Escape string and wrap the result in quotes.
@@ -14,11 +14,5 @@ export default function escapeString(string, quote = '\'') {
14
14
  }
15
15
  /* c8 ignore end */
16
16
 
17
- return jsesc(string, {
18
- quotes: quote === '"' ? 'double' : 'single',
19
- wrap: true,
20
- es6: true,
21
- minimal: true,
22
- lowercaseHex: false,
23
- });
17
+ return quoteJsString(string, quote);
24
18
  }
@@ -1,13 +1,7 @@
1
- import helperValidatorIdentifier from '@babel/helper-validator-identifier';
1
+ import isIdentifier from 'is-identifier';
2
2
  import resolveVariableName from './resolve-variable-name.js';
3
3
  import getReferences from './get-references.js';
4
4
 
5
- const {
6
- isIdentifierName,
7
- isStrictReservedWord,
8
- isKeyword,
9
- } = helperValidatorIdentifier;
10
-
11
5
  // https://github.com/microsoft/TypeScript/issues/2536#issuecomment-87194347
12
6
  const typescriptReservedWords = new Set([
13
7
  'break',
@@ -72,14 +66,9 @@ const typescriptReservedWords = new Set([
72
66
  'of',
73
67
  ]);
74
68
 
75
- // Copied from https://github.com/babel/babel/blob/fce35af69101c6b316557e28abf60bdbf77d6a36/packages/babel-types/src/validators/isValidIdentifier.ts#L7
76
- // Use this function instead of `require('@babel/types').isIdentifier`, since `@babel/helper-validator-identifier` package is much smaller
77
69
  const isValidIdentifier = name =>
78
70
  typeof name === 'string'
79
- && !isKeyword(name)
80
- && !isStrictReservedWord(name, true)
81
- && isIdentifierName(name)
82
- && name !== 'arguments'
71
+ && isIdentifier(name)
83
72
  && !typescriptReservedWords.has(name);
84
73
 
85
74
  /*