Skip to content

Commit

Permalink
feat(eslint-plugin): [prefer-string-starts-ends-with] add allowSingle…
Browse files Browse the repository at this point in the history
…ElementEquality option (#8374)

* feat(eslint-plugin): [prefer-string-starts-ends-with] add allowSingleElementEquality option

* Update test snapshots

* Update packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md

Co-authored-by: auvred <61150013+auvred@users.noreply.github.com>

---------

Co-authored-by: auvred <61150013+auvred@users.noreply.github.com>
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
3 people committed Mar 5, 2024
1 parent 4c8b06d commit 32d8a8f
Show file tree
Hide file tree
Showing 6 changed files with 404 additions and 259 deletions.
7 changes: 6 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ module.exports = {

// TODO(#7130): Investigate changing these in or removing these from presets
'@typescript-eslint/no-confusing-void-expression': 'off',
'@typescript-eslint/prefer-string-starts-ends-with': 'off',

//
// our plugin :D
Expand Down Expand Up @@ -91,6 +90,12 @@ module.exports = {
allowBitwiseExpressions: true,
},
],
'@typescript-eslint/prefer-string-starts-ends-with': [
'error',
{
allowSingleElementEquality: 'always',
},
],
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/restrict-template-expressions': [
'error',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,26 @@ foo.startsWith('bar');
foo.endsWith('bar');
```

<!--/tabs-->

## Options

### `allowSingleElementEquality`

If switched to `'always'`, the rule will allow equality checks against the first or last character in a string.
This can be preferable in projects that don't deal with special character encodings and prefer a more succinct style.

The following code is considered incorrect by default, but is allowed with `allowSingleElementEquality: 'always'`:

```ts option='{ "allowSingleElementEquality": "always" }' showPlaygroundButton
declare const text: string;

text[0] === 'a';
text[0] === text[0].toUpperCase();
text[0] === text[1];
text[text.length - 1] === 'b';
```

## When Not To Use It

If you don't mind which style of string checking is used, you can turn this rule off safely.
Expand Down
40 changes: 35 additions & 5 deletions packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ import {
const EQ_OPERATORS = /^[=!]=/;
const regexpp = new RegExpParser();

export default createRule({
type AllowedSingleElementEquality = 'always' | 'never';

export type Options = [
{
allowSingleElementEquality?: AllowedSingleElementEquality;
},
];

type MessageIds = 'preferEndsWith' | 'preferStartsWith';

export default createRule<Options, MessageIds>({
name: 'prefer-string-starts-ends-with',
defaultOptions: [],
defaultOptions: [{ allowSingleElementEquality: 'never' }],

meta: {
type: 'suggestion',
Expand All @@ -33,11 +43,24 @@ export default createRule({
preferStartsWith: "Use 'String#startsWith' method instead.",
preferEndsWith: "Use the 'String#endsWith' method instead.",
},
schema: [],
schema: [
{
additionalProperties: false,
properties: {
allowSingleElementEquality: {
description:
'Whether to allow equality checks against the first or last element of a string.',
enum: ['always', 'never'],
type: 'string',
},
},
type: 'object',
},
],
fixable: 'code',
},

create(context) {
create(context, [{ allowSingleElementEquality }]) {
const globalScope = context.sourceCode.getScope(context.sourceCode.ast);

const services = getParserServices(context);
Expand Down Expand Up @@ -401,8 +424,15 @@ export default createRule({
}

const isEndsWith = isLastIndexExpression(indexNode, node.object);
if (allowSingleElementEquality === 'always' && isEndsWith) {
return;
}

const isStartsWith = !isEndsWith && isNumber(indexNode, 0);
if (!isStartsWith && !isEndsWith) {
if (
(allowSingleElementEquality === 'always' && isStartsWith) ||
(!isStartsWith && !isEndsWith)
) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/util/collectUnusedVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ function isExported(variable: TSESLint.Scope.Variable): boolean {
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return node.parent!.type.indexOf('Export') === 0;
return node.parent!.type.startsWith('Export');
});
}

Expand Down

0 comments on commit 32d8a8f

Please sign in to comment.