diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a4b8dcc525..1856cb48a0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,7 @@ --- name: Bug report about: A rule isn't working as it should? +labels: bug --- diff --git a/.github/ISSUE_TEMPLATE/rule_change.yml b/.github/ISSUE_TEMPLATE/rule_change.yml new file mode 100644 index 0000000000..fc9fab8f2d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rule_change.yml @@ -0,0 +1,55 @@ +name: Rule change +description: A rule could be improved or extended? +title: "`rule-name`: " +labels: + - change request + - evaluating +body: + - type: markdown + attributes: + value: | + Before opening a new issue: + - Look for existing [open or closed rule proposals](https://github.com/sindresorhus/eslint-plugin-unicorn/issues?q=is:issue+label%25enhancement) + - [Report a bug](https://github.com/sindresorhus/eslint-plugin-unicorn/issues/new?assignees=&labels=&projects=&template=bug_report.md) instead if it's a false positive + - type: textarea + validations: + required: true + attributes: + label: Description + description: Explain the improvement you would like to see. + - type: textarea + validations: + required: true + attributes: + label: Fail + description: Specify examples of code that should be detected. + value: | + ```js + var replace = 'me'; + ``` + + ```js + function foo() { + var replace = 'me'; + return replace; + } + ``` + - type: textarea + validations: + required: true + attributes: + label: Pass + description: Specify examples of code that would be accepted in its place. + value: | + ```js + const replace = 'me'; + ``` + + ```js + function foo() { + return 'me'; + } + ``` + - type: textarea + attributes: + label: Additional Info diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d33c82ba1..bd0cf3e0a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - run: npm install --legacy-peer-deps + - run: npm install - run: npx ava lint-test: runs-on: ${{ matrix.os }} @@ -44,7 +44,7 @@ jobs: with: # Locked due to the difference of `zlib.gzipSync()` between Node.js versions node-version: 20 - - run: npm install --legacy-peer-deps + - run: npm install - run: npm run lint - run: npx del-cli test/snapshots --verbose # Force update snapshots, https://github.com/avajs/ava/discussions/2754 @@ -62,7 +62,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - - run: npm install --legacy-peer-deps + - run: npm install - run: npm run run-rules-on-codebase integration: name: Integration test (${{ matrix.group }}) @@ -85,5 +85,5 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - - run: npm install --legacy-peer-deps + - run: npm install - run: npm run integration -- --group ${{ matrix.group }} diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index 04104b0abc..d8a56aa0cb 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -9,13 +9,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - run: | - npm install --legacy-peer-deps - npm link - npm link eslint-plugin-unicorn --legacy-peer-deps - - uses: AriPerkkio/eslint-remote-tester-run-action@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - run: npm install + - uses: AriPerkkio/eslint-remote-tester-run-action@v5 with: issue-title: "Results of weekly scheduled smoke test" - eslint-remote-tester-config: test/smoke/eslint-remote-tester.config.js + eslint-remote-tester-config: test/smoke/eslint-remote-tester.config.mjs diff --git a/docs/rules/no-negation-in-equality-check.md b/docs/rules/no-negation-in-equality-check.md new file mode 100644 index 0000000000..248261ebeb --- /dev/null +++ b/docs/rules/no-negation-in-equality-check.md @@ -0,0 +1,30 @@ +# Disallow negated expression in equality check + +πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). + +πŸ’‘ This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). + + + + +Using a negated expression in equality check is most likely a mistake. + +## Fail + +```js +if (!foo === bar) {} +``` + +```js +if (!foo !== bar) {} +``` + +## Pass + +```js +if (foo !== bar) {} +``` + +```js +if (!(foo === bar)) {} +``` diff --git a/docs/rules/prefer-array-find.md b/docs/rules/prefer-array-find.md index 5b7065e03c..4f4b1db54f 100644 --- a/docs/rules/prefer-array-find.md +++ b/docs/rules/prefer-array-find.md @@ -17,10 +17,18 @@ This rule is fixable unless default values are used in declaration or assignment const item = array.filter(x => isUnicorn(x))[0]; ``` +```js +const item = array.filter(x => isUnicorn(x)).at(-1); +``` + ```js const item = array.filter(x => isUnicorn(x)).shift(); ``` +```js +const item = array.filter(x => isUnicorn(x)).pop(); +``` + ```js const [item] = array.filter(x => isUnicorn(x)); ``` @@ -50,25 +58,18 @@ Type: `object` ### checkFromLast Type: `boolean`\ -Default: `false` +Default: `true` -Pass `checkFromLast: true` to check cases searching from last. +Pass `checkFromLast: false` to disable check cases searching from last. -#### Fail +#### Pass ```js -// eslint unicorn/prefer-array-find: ["error", {"checkFromLast": true}] +// eslint unicorn/prefer-array-find: ["error", {"checkFromLast": false}] const item = array.filter(x => isUnicorn(x)).at(-1); ``` ```js -// eslint unicorn/prefer-array-find: ["error", {"checkFromLast": true}] +// eslint unicorn/prefer-array-find: ["error", {"checkFromLast": false}] const item = array.filter(x => isUnicorn(x)).pop(); ``` - -#### Pass - -```js -// eslint unicorn/prefer-array-find: ["error", {"checkFromLast": true}] -const item = array.findLast(x => isUnicorn(x)); -``` diff --git a/docs/rules/prefer-array-some.md b/docs/rules/prefer-array-some.md index cc11083488..e7b1899483 100644 --- a/docs/rules/prefer-array-some.md +++ b/docs/rules/prefer-array-some.md @@ -1,4 +1,4 @@ -# Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast}(…)` +# Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast,findIndex,findLastIndex}(…)` πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). @@ -17,7 +17,11 @@ We only check `.filter().length > 0` and `.filter().length !== 0`. These two non - Comparing the result of [`Array#find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) or [`Array#findLast()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) with `undefined`. -This rule is fixable for `.filter(…).length` check and has a suggestion for `.{find,findLast}(…)`. +- Using [`Array#findIndex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) or [`Array#findLastIndex()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex) to ensure at least one element in the array passes a given check. + +This rule is fixable for `.filter(…).length` checks and `.{findIndex,findLastIndex}(…)`. + +This rule provides a suggestion for `.{find,findLast}(…)`. ## Fail @@ -44,11 +48,11 @@ const foo = array.find(element => isUnicorn(element)) ? bar : baz; ``` ```js -const hasUnicorn = array.find(element => isUnicorn(element) !== undefined; +const hasUnicorn = array.find(element => isUnicorn(element)) !== undefined; ``` ```js -const hasUnicorn = array.find(element => isUnicorn(element) != null; +const hasUnicorn = array.find(element => isUnicorn(element)) != null; ``` ```js @@ -62,11 +66,19 @@ const foo = array.findLast(element => isUnicorn(element)) ? bar : baz; ``` ```js -const hasUnicorn = array.findLast(element => isUnicorn(element) !== undefined; +const hasUnicorn = array.findLast(element => isUnicorn(element)) !== undefined; +``` + +```js +const hasUnicorn = array.findLast(element => isUnicorn(element)) != null; +``` + +```js +const hasUnicorn = array.findIndex(element => isUnicorn(element)) !== -1; ``` ```js -const hasUnicorn = array.findLast(element => isUnicorn(element) != null; +const hasUnicorn = array.findLastIndex(element => isUnicorn(element)) !== -1; ``` ```vue diff --git a/docs/rules/prefer-includes.md b/docs/rules/prefer-includes.md index f23d27aec3..4c8f48d323 100644 --- a/docs/rules/prefer-includes.md +++ b/docs/rules/prefer-includes.md @@ -1,4 +1,4 @@ -# Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence +# Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence πŸ’Ό This rule is enabled in the βœ… `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs-eslintconfigjs). @@ -7,7 +7,7 @@ -All built-ins have `.includes()` in addition to `.indexOf()`. Prefer `.includes()` over comparing the value of `.indexOf()`. +All built-ins have `.includes()` in addition to `.indexOf()` and `.lastIndexOf()`. Prefer `.includes()` over comparing the value of `.indexOf()` and `.lastIndexOf()`. [`Array#some()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) is intended for more complex needs. If you are just looking for the index where the given item is present, the code can be simplified to use [`Array#includes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes). This applies to any search with a literal, a variable, or any expression that doesn't have any explicit side effects. However, if the expression you are looking for relies on an item related to the function (its arguments, the function self, etc.), the case is still valid. @@ -16,99 +16,103 @@ This rule is fixable, unless the search expression in `Array#some()` has side ef ## Fail ```js -[].indexOf('foo') !== -1; +array.indexOf('foo') !== -1; ``` ```js -x.indexOf('foo') != -1; +array.indexOf('foo') !== -1; ``` ```js -str.indexOf('foo') > -1; +string.lastIndexOf('foo') !== -1; ``` ```js -'foobar'.indexOf('foo') >= 0; +array.lastIndexOf('foo') !== -1; ``` ```js -x.indexOf('foo') === -1 +foo.indexOf('foo') != -1; ``` ```js -const isFound = foo.some(x => x === 'foo'); +foo.indexOf('foo') >= 0; ``` ```js -const isFound = foo.some(x => 'foo' === x); +foo.indexOf('foo') > -1; ``` ```js -const isFound = foo.some(x => { - return x === 'foo'; -}); +foo.indexOf('foo') === -1 ``` -## Pass +```js +foo.some(x => x === 'foo'); +``` ```js -const str = 'foobar'; +foo.some(x => 'foo' === x); ``` ```js -str.indexOf('foo') !== -n; +foo.some(x => { + return x === 'foo'; +}); ``` +## Pass + ```js -str.indexOf('foo') !== 1; +foo.indexOf('foo') !== -n; ``` ```js -!str.indexOf('foo') === 1; +foo.indexOf('foo') !== 1; ``` ```js -!str.indexOf('foo') === -n; +foo.indexOf('foo') === 1; ``` ```js -str.includes('foo'); +foo.includes('foo'); ``` ```js -[1,2,3].includes(4); +foo.includes(4); ``` ```js -const isFound = foo.includes('foo'); +foo.includes('foo'); ``` ```js -const isFound = foo.some(x => x == undefined); +foo.some(x => x == undefined); ``` ```js -const isFound = foo.some(x => x !== 'foo'); +foo.some(x => x !== 'foo'); ``` ```js -const isFound = foo.some((x, index) => x === index); +foo.some((x, index) => x === index); ``` ```js -const isFound = foo.some(x => (x === 'foo') && isValid()); +foo.some(x => (x === 'foo') && isValid()); ``` ```js -const isFound = foo.some(x => y === 'foo'); +foo.some(x => y === 'foo'); ``` ```js -const isFound = foo.some(x => y.x === 'foo'); +foo.some(x => y.x === 'foo'); ``` ```js -const isFound = foo.some(x => { +foo.some(x => { const bar = getBar(); return x === bar; }); diff --git a/index.js b/index.js index 0f754a2077..77bc1b1a42 100644 --- a/index.js +++ b/index.js @@ -47,9 +47,12 @@ const allRules = Object.fromEntries( ]), ); -const createConfig = (rules, isLegacyConfig = false) => ({ - ...(isLegacyConfig ? legacyConfigBase : flatConfigBase), - plugins: isLegacyConfig ? ['unicorn'] : {unicorn}, +const createConfig = (rules, flatConfigName = false) => ({ + ...( + flatConfigName + ? {...flatConfigBase, name: flatConfigName, plugins: {unicorn}} + : {...legacyConfigBase, plugins: ['unicorn']} + ), rules: {...externalRules, ...rules}, }); @@ -65,10 +68,10 @@ const unicorn = { }; const configs = { - recommended: createConfig(recommendedRules, /* isLegacyConfig */ true), - all: createConfig(allRules, /* isLegacyConfig */ true), - 'flat/recommended': createConfig(recommendedRules), - 'flat/all': createConfig(allRules), + recommended: createConfig(recommendedRules), + all: createConfig(allRules), + 'flat/recommended': createConfig(recommendedRules, 'unicorn/flat/recommended'), + 'flat/all': createConfig(allRules, 'unicorn/flat/all'), }; module.exports = {...unicorn, configs}; diff --git a/package.json b/package.json index c0205f9052..205dbb79bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-unicorn", - "version": "53.0.0", + "version": "54.0.0", "description": "More than 100 powerful ESLint rules", "license": "MIT", "repository": "sindresorhus/eslint-plugin-unicorn", @@ -29,7 +29,7 @@ "lint:markdown": "markdownlint \"**/*.md\"", "lint:package-json": "npmPkgJsonLint .", "run-rules-on-codebase": "node ./test/run-rules-on-codebase/lint.mjs", - "smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.js", + "smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.mjs", "test": "npm-run-all --continue-on-error lint test:*", "test:js": "c8 ava" }, @@ -71,7 +71,7 @@ "@babel/core": "^7.24.5", "@babel/eslint-parser": "^7.24.5", "@lubien/fixture-beta-package": "^1.0.0-beta.1", - "@typescript-eslint/parser": "^7.8.0", + "@typescript-eslint/parser": "^8.0.0-alpha.12", "ava": "^6.1.3", "c8": "^9.1.0", "chalk": "^5.3.0", @@ -81,8 +81,8 @@ "eslint-doc-generator": "1.7.0", "eslint-plugin-eslint-plugin": "^6.1.0", "eslint-plugin-internal-rules": "file:./scripts/internal-rules/", - "eslint-remote-tester": "^3.0.1", - "eslint-remote-tester-repositories": "^1.0.1", + "eslint-remote-tester": "^4.0.0", + "eslint-remote-tester-repositories": "^2.0.0", "espree": "^10.0.1", "execa": "^8.0.1", "listr": "^0.14.3", diff --git a/readme.md b/readme.md index f21ccaae94..bdfcb60e9a 100644 --- a/readme.md +++ b/readme.md @@ -145,6 +145,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [no-lonely-if](docs/rules/no-lonely-if.md) | Disallow `if` statements as the only statement in `if` blocks without `else`. | βœ… | πŸ”§ | | | [no-magic-array-flat-depth](docs/rules/no-magic-array-flat-depth.md) | Disallow a magic number as the `depth` argument in `Array#flat(…).` | βœ… | | | | [no-negated-condition](docs/rules/no-negated-condition.md) | Disallow negated conditions. | βœ… | πŸ”§ | | +| [no-negation-in-equality-check](docs/rules/no-negation-in-equality-check.md) | Disallow negated expression in equality check. | βœ… | | πŸ’‘ | | [no-nested-ternary](docs/rules/no-nested-ternary.md) | Disallow nested ternary expressions. | βœ… | πŸ”§ | | | [no-new-array](docs/rules/no-new-array.md) | Disallow `new Array()`. | βœ… | πŸ”§ | πŸ’‘ | | [no-new-buffer](docs/rules/no-new-buffer.md) | Enforce the use of `Buffer.from()` and `Buffer.alloc()` instead of the deprecated `new Buffer()`. | βœ… | πŸ”§ | πŸ’‘ | @@ -175,7 +176,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [prefer-array-flat](docs/rules/prefer-array-flat.md) | Prefer `Array#flat()` over legacy techniques to flatten arrays. | βœ… | πŸ”§ | | | [prefer-array-flat-map](docs/rules/prefer-array-flat-map.md) | Prefer `.flatMap(…)` over `.map(…).flat()`. | βœ… | πŸ”§ | | | [prefer-array-index-of](docs/rules/prefer-array-index-of.md) | Prefer `Array#{indexOf,lastIndexOf}()` over `Array#{findIndex,findLastIndex}()` when looking for the index of an item. | βœ… | πŸ”§ | πŸ’‘ | -| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast}(…)`. | βœ… | πŸ”§ | πŸ’‘ | +| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast,findIndex,findLastIndex}(…)`. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-at](docs/rules/prefer-at.md) | Prefer `.at()` method for index access and `String#charAt()`. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-blob-reading-methods](docs/rules/prefer-blob-reading-methods.md) | Prefer `Blob#arrayBuffer()` over `FileReader#readAsArrayBuffer(…)` and `Blob#text()` over `FileReader#readAsText(…)`. | βœ… | | | | [prefer-code-point](docs/rules/prefer-code-point.md) | Prefer `String#codePointAt(…)` over `String#charCodeAt(…)` and `String.fromCodePoint(…)` over `String.fromCharCode(…)`. | βœ… | | πŸ’‘ | @@ -187,7 +188,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c | [prefer-dom-node-text-content](docs/rules/prefer-dom-node-text-content.md) | Prefer `.textContent` over `.innerText`. | βœ… | | πŸ’‘ | | [prefer-event-target](docs/rules/prefer-event-target.md) | Prefer `EventTarget` over `EventEmitter`. | βœ… | | | | [prefer-export-from](docs/rules/prefer-export-from.md) | Prefer `export…from` when re-exporting. | βœ… | πŸ”§ | πŸ’‘ | -| [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence. | βœ… | πŸ”§ | πŸ’‘ | +| [prefer-includes](docs/rules/prefer-includes.md) | Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence. | βœ… | πŸ”§ | πŸ’‘ | | [prefer-json-parse-buffer](docs/rules/prefer-json-parse-buffer.md) | Prefer reading a JSON file as a buffer. | | πŸ”§ | | | [prefer-keyboard-event-key](docs/rules/prefer-keyboard-event-key.md) | Prefer `KeyboardEvent#key` over `KeyboardEvent#keyCode`. | βœ… | πŸ”§ | | | [prefer-logical-operator-over-ternary](docs/rules/prefer-logical-operator-over-ternary.md) | Prefer using a logical operator over a ternary. | βœ… | | πŸ’‘ | diff --git a/rules/ast/is-reference-identifier.js b/rules/ast/is-reference-identifier.js index 7ebf894119..0794a3acd7 100644 --- a/rules/ast/is-reference-identifier.js +++ b/rules/ast/is-reference-identifier.js @@ -120,11 +120,18 @@ function isNotReference(node) { return parent.parameters.includes(node); } + // `@typescript-eslint/parse` v7 // `type Foo = { [Identifier in keyof string]: number; };` case 'TSTypeParameter': { return parent.name === node; } + // `@typescript-eslint/parse` v8 + // `type Foo = { [Identifier in keyof string]: number; };` + case 'TSMappedType': { + return parent.key === node; + } + // `type Identifier = Foo` case 'TSTypeAliasDeclaration': { return parent.id === node; diff --git a/rules/fix/switch-call-expression-to-new-expression.js b/rules/fix/switch-call-expression-to-new-expression.js index fc3073527c..813e3dfdf0 100644 --- a/rules/fix/switch-call-expression-to-new-expression.js +++ b/rules/fix/switch-call-expression-to-new-expression.js @@ -1,8 +1,10 @@ 'use strict'; const {isParenthesized} = require('../utils/parentheses.js'); const shouldAddParenthesesToNewExpressionCallee = require('../utils/should-add-parentheses-to-new-expression-callee.js'); +const fixSpaceAroundKeyword = require('./fix-space-around-keywords.js'); function * switchCallExpressionToNewExpression(node, sourceCode, fixer) { + yield * fixSpaceAroundKeyword(fixer, node, sourceCode); yield fixer.insertTextBefore(node, 'new '); const {callee} = node; diff --git a/rules/no-negation-in-equality-check.js b/rules/no-negation-in-equality-check.js new file mode 100644 index 0000000000..9bcdbb8115 --- /dev/null +++ b/rules/no-negation-in-equality-check.js @@ -0,0 +1,104 @@ +'use strict'; +const { + fixSpaceAroundKeyword, + addParenthesizesToReturnOrThrowExpression, +} = require('./fix/index.js'); +const { + needsSemicolon, + isParenthesized, + isOnSameLine, +} = require('./utils/index.js'); + +const MESSAGE_ID_ERROR = 'no-negation-in-equality-check/error'; +const MESSAGE_ID_SUGGESTION = 'no-negation-in-equality-check/suggestion'; +const messages = { + [MESSAGE_ID_ERROR]: 'Negated expression in not allowed in equality check.', + [MESSAGE_ID_SUGGESTION]: 'Switch to \'{{operator}}\' check.', +}; + +const EQUALITY_OPERATORS = new Set([ + '===', + '!==', + '==', + '!=', +]); + +const isEqualityCheck = node => node.type === 'BinaryExpression' && EQUALITY_OPERATORS.has(node.operator); +const isNegatedExpression = node => node.type === 'UnaryExpression' && node.prefix && node.operator === '!'; + +/** @param {import('eslint').Rule.RuleContext} context */ +const create = context => ({ + BinaryExpression(binaryExpression) { + const {operator, left} = binaryExpression; + + if ( + !isEqualityCheck(binaryExpression) + || !isNegatedExpression(left) + ) { + return; + } + + const {sourceCode} = context; + const bangToken = sourceCode.getFirstToken(left); + const negatedOperator = `${operator.startsWith('!') ? '=' : '!'}${operator.slice(1)}`; + + return { + node: bangToken, + messageId: MESSAGE_ID_ERROR, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + suggest: [ + { + messageId: MESSAGE_ID_SUGGESTION, + data: { + operator: negatedOperator, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + * fix(fixer) { + yield * fixSpaceAroundKeyword(fixer, binaryExpression, sourceCode); + + const tokenAfterBang = sourceCode.getTokenAfter(bangToken); + + const {parent} = binaryExpression; + if ( + (parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement') + && !isParenthesized(binaryExpression, sourceCode) + ) { + const returnToken = sourceCode.getFirstToken(parent); + if (!isOnSameLine(returnToken, tokenAfterBang)) { + yield * addParenthesizesToReturnOrThrowExpression(fixer, parent, sourceCode); + } + } + + yield fixer.remove(bangToken); + + const previousToken = sourceCode.getTokenBefore(bangToken); + if (needsSemicolon(previousToken, sourceCode, tokenAfterBang.value)) { + yield fixer.insertTextAfter(bangToken, ';'); + } + + const operatorToken = sourceCode.getTokenAfter( + left, + token => token.type === 'Punctuator' && token.value === operator, + ); + yield fixer.replaceText(operatorToken, negatedOperator); + }, + }, + ], + }; + }, +}); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + create, + meta: { + type: 'problem', + docs: { + description: 'Disallow negated expression in equality check.', + recommended: true, + }, + + hasSuggestions: true, + messages, + }, +}; diff --git a/rules/prefer-array-find.js b/rules/prefer-array-find.js index 736ac4b8c5..85ff943cf1 100644 --- a/rules/prefer-array-find.js +++ b/rules/prefer-array-find.js @@ -180,7 +180,7 @@ const create = context => { const { checkFromLast, } = { - checkFromLast: false, + checkFromLast: true, ...context.options[0], }; @@ -428,8 +428,8 @@ const schema = [ properties: { checkFromLast: { type: 'boolean', - // TODO: Change default value to `true`, or remove the option when targeting Node.js 18. - default: false, + // TODO: Remove the option at some point. + default: true, }, }, }, diff --git a/rules/prefer-array-flat.js b/rules/prefer-array-flat.js index 2bf40baca9..854ff24a80 100644 --- a/rules/prefer-array-flat.js +++ b/rules/prefer-array-flat.js @@ -46,7 +46,6 @@ const arrayFlatMap = { }, getArrayNode: node => node.callee.object, description: 'Array#flatMap()', - recommended: true, }; // `array.reduce((a, b) => a.concat(b), [])` @@ -100,7 +99,6 @@ const arrayReduce = { }, getArrayNode: node => node.callee.object, description: 'Array#reduce()', - recommended: true, }; // `[].concat(maybeArray)` @@ -121,7 +119,6 @@ const emptyArrayConcat = { return argumentNode.type === 'SpreadElement' ? argumentNode.argument : argumentNode; }, description: '[].concat()', - recommended: true, shouldSwitchToArray: node => node.arguments[0].type !== 'SpreadElement', }; @@ -157,7 +154,6 @@ const arrayPrototypeConcat = { return argumentNode.type === 'SpreadElement' ? argumentNode.argument : argumentNode; }, description: 'Array.prototype.concat()', - recommended: true, shouldSwitchToArray: node => node.arguments[1].type !== 'SpreadElement' && node.callee.property.name === 'call', }; diff --git a/rules/prefer-array-some.js b/rules/prefer-array-some.js index 563dd39b79..3da1cf5409 100644 --- a/rules/prefer-array-some.js +++ b/rules/prefer-array-some.js @@ -40,10 +40,14 @@ const isCheckingUndefined = node => && isLiteral(node.parent.right, null) ) ); +const isNegativeOne = node => node.type === 'UnaryExpression' && node.operator === '-' && node.argument && node.argument.type === 'Literal' && node.argument.value === 1; +const isLiteralZero = node => isLiteral(node, 0); /** @param {import('eslint').Rule.RuleContext} context */ -const create = context => ({ - CallExpression(callExpression) { +const create = context => { + // `.find(…)` + // `.findLast(…)` + context.on('CallExpression', callExpression => { if (!isMethodCall(callExpression, { methods: ['find', 'findLast'], minimumArguments: 1, @@ -86,8 +90,61 @@ const create = context => ({ }, ], }; - }, - BinaryExpression(binaryExpression) { + }); + + // These operators also used in `prefer-includes`, try to reuse the code in future + // `.{findIndex,findLastIndex}(…) !== -1` + // `.{findIndex,findLastIndex}(…) != -1` + // `.{findIndex,findLastIndex}(…) > -1` + // `.{findIndex,findLastIndex}(…) === -1` + // `.{findIndex,findLastIndex}(…) == -1` + // `.{findIndex,findLastIndex}(…) >= 0` + // `.{findIndex,findLastIndex}(…) < 0` + context.on('BinaryExpression', binaryExpression => { + const {left, right, operator} = binaryExpression; + + if (!( + isMethodCall(left, { + methods: ['findIndex', 'findLastIndex'], + argumentsLength: 1, + optionalCall: false, + optionalMember: false, + }) + && ( + (['!==', '!=', '>', '===', '=='].includes(operator) && isNegativeOne(right)) + || (['>=', '<'].includes(operator) && isLiteralZero(right)) + ) + )) { + return; + } + + const methodNode = left.callee.property; + return { + node: methodNode, + messageId: ERROR_ID_ARRAY_SOME, + data: {method: methodNode.name}, + * fix(fixer) { + if (['===', '==', '<'].includes(operator)) { + yield fixer.insertTextBefore(binaryExpression, '!'); + } + + yield fixer.replaceText(methodNode, 'some'); + + const operatorToken = context.sourceCode.getTokenAfter( + left, + token => token.type === 'Punctuator' && token.value === operator, + ); + const [start] = operatorToken.range; + const [, end] = binaryExpression.range; + + yield fixer.removeRange([start, end]); + }, + }; + }); + + // `.filter(…).length > 0` + // `.filter(…).length !== 0` + context.on('BinaryExpression', binaryExpression => { if (!( // We assume the user already follows `unicorn/explicit-length-check`. These are allowed in that rule. (binaryExpression.operator === '>' || binaryExpression.operator === '!==') @@ -139,8 +196,8 @@ const create = context => ({ // The `BinaryExpression` always ends with a number or `)`, no need check for ASI }, }; - }, -}); + }); +}; /** @type {import('eslint').Rule.RuleModule} */ module.exports = { @@ -148,7 +205,7 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast}(…)`.', + description: 'Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast,findIndex,findLastIndex}(…)`.', recommended: true, }, fixable: 'code', diff --git a/rules/prefer-includes.js b/rules/prefer-includes.js index 0cfd14a6c2..e9e878beb0 100644 --- a/rules/prefer-includes.js +++ b/rules/prefer-includes.js @@ -5,9 +5,9 @@ const {isLiteral} = require('./ast/index.js'); const MESSAGE_ID = 'prefer-includes'; const messages = { - [MESSAGE_ID]: 'Use `.includes()`, rather than `.indexOf()`, when checking for existence.', + [MESSAGE_ID]: 'Use `.includes()`, rather than `.{{method}}()`, when checking for existence.', }; -// Ignore {_,lodash,underscore}.indexOf +// Ignore `{_,lodash,underscore}.{indexOf,lastIndexOf}` const ignoredVariables = new Set(['_', 'lodash', 'underscore']); const isIgnoredTarget = node => node.type === 'Identifier' && ignoredVariables.has(node.name); const isNegativeOne = node => node.type === 'UnaryExpression' && node.operator === '-' && node.argument && node.argument.type === 'Literal' && node.argument.value === 1; @@ -30,6 +30,9 @@ const getProblem = (context, node, target, argumentsNodes) => { return { node: memberExpressionNode.property, messageId: MESSAGE_ID, + data: { + method: node.left.callee.property.name, + }, fix(fixer) { const replacement = `${isNegativeResult(node) ? '!' : ''}${targetSource}.includes(${argumentsSource.join(', ')})`; return fixer.replaceText(node, replacement); @@ -49,7 +52,7 @@ const create = context => { context.on('BinaryExpression', node => { const {left, right, operator} = node; - if (!isMethodNamed(left, 'indexOf')) { + if (!isMethodNamed(left, 'indexOf') && !isMethodNamed(left, 'lastIndexOf')) { return; } @@ -86,7 +89,7 @@ module.exports = { meta: { type: 'suggestion', docs: { - description: 'Prefer `.includes()` over `.indexOf()` and `Array#some()` when checking for existence or non-existence.', + description: 'Prefer `.includes()` over `.indexOf()`, `.lastIndexOf()`, and `Array#some()` when checking for existence or non-existence.', recommended: true, }, fixable: 'code', diff --git a/rules/prefer-modern-math-apis.js b/rules/prefer-modern-math-apis.js index 232803e1f0..1cb6fb605b 100644 --- a/rules/prefer-modern-math-apis.js +++ b/rules/prefer-modern-math-apis.js @@ -154,7 +154,6 @@ const create = context => { data: { replacement: `Math.${replacementMethod}(…)`, description: 'Math.sqrt(…)', - recommended: true, }, * fix(fixer) { const {sourceCode} = context; diff --git a/rules/prefer-string-raw.js b/rules/prefer-string-raw.js index 4241e11163..134e0e5d02 100644 --- a/rules/prefer-string-raw.js +++ b/rules/prefer-string-raw.js @@ -47,6 +47,7 @@ const create = context => { ) || (node.parent.type === 'Property' && !node.parent.computed && node.parent.key === node) || (node.parent.type === 'JSXAttribute' && node.parent.value === node) + || (node.parent.type === 'TSEnumMember' && (node.parent.initializer === node || node.parent.id === node)) ) { return; } diff --git a/rules/prefer-structured-clone.js b/rules/prefer-structured-clone.js index e66f54e930..70b7768c42 100644 --- a/rules/prefer-structured-clone.js +++ b/rules/prefer-structured-clone.js @@ -61,7 +61,6 @@ const create = context => { messageId: MESSAGE_ID_ERROR, data: { description: 'JSON.parse(JSON.stringify(…))', - recommended: true, }, suggest: [ { diff --git a/test/new-for-builtins.mjs b/test/new-for-builtins.mjs index fa042643da..f737fc28e1 100644 --- a/test/new-for-builtins.mjs +++ b/test/new-for-builtins.mjs @@ -231,26 +231,31 @@ test.snapshot({ 'const foo = new Number(\'123\')', 'const foo = new String()', 'const foo = new Symbol()', - ` - function varCheck() { - { - var WeakMap = function() {}; - } - // This should not reported - return WeakMap() + outdent` + function varCheck() { + { + var WeakMap = function() {}; } - function constCheck() { - { - const Array = function() {}; - } - return Array() + // This should not reported + return WeakMap() + } + function constCheck() { + { + const Array = function() {}; } - function letCheck() { - { - let Map = function() {}; - } - return Map() + return Array() + } + function letCheck() { + { + let Map = function() {}; } - `, + return Map() + } + `, + outdent` + function foo() { + return(globalThis).Map() + } + `, ], }); diff --git a/test/no-negation-in-equality-check.mjs b/test/no-negation-in-equality-check.mjs new file mode 100644 index 0000000000..70d0e58025 --- /dev/null +++ b/test/no-negation-in-equality-check.mjs @@ -0,0 +1,50 @@ +import outdent from 'outdent'; +import {getTester} from './utils/test.mjs'; + +const {test} = getTester(import.meta); + +test.snapshot({ + valid: [ + '!foo instanceof bar', + '+foo === bar', + '!(foo === bar)', + // We are not checking right side + 'foo === !bar', + ], + invalid: [ + '!foo === bar', + '!foo !== bar', + '!foo == bar', + '!foo != bar', + outdent` + function x() { + return!foo === bar; + } + `, + outdent` + function x() { + return! + foo === bar; + throw! + foo === bar; + } + `, + outdent` + foo + !(a) === b + `, + outdent` + foo + ![a, b].join('') === c + `, + outdent` + foo + ! [a, b].join('') === c + `, + outdent` + foo + !/* comment */[a, b].join('') === c + `, + '!!foo === bar', + ], +}); diff --git a/test/package.mjs b/test/package.mjs index 2dad8894fd..33cd39cfe9 100644 --- a/test/package.mjs +++ b/test/package.mjs @@ -171,11 +171,11 @@ function getCompactConfig(config) { test('flat configs', t => { t.deepEqual( - getCompactConfig(eslintPluginUnicorn.configs.recommended), + {...getCompactConfig(eslintPluginUnicorn.configs.recommended), name: 'unicorn/flat/recommended'}, {...eslintPluginUnicorn.configs['flat/recommended'], plugins: undefined}, ); t.deepEqual( - getCompactConfig(eslintPluginUnicorn.configs.all), + {...getCompactConfig(eslintPluginUnicorn.configs.all), name: 'unicorn/flat/all'}, {...eslintPluginUnicorn.configs['flat/all'], plugins: undefined}, ); }); diff --git a/test/prefer-array-find.mjs b/test/prefer-array-find.mjs index e886d293ee..a10a6fc7ee 100644 --- a/test/prefer-array-find.mjs +++ b/test/prefer-array-find.mjs @@ -923,15 +923,12 @@ test({ ], }); -// Check from last -const checkFromLastOptions = [{checkFromLast: true}]; - -// Default to false +// `checkFromLast` default to true test({ valid: [ 'array.filter(foo).pop()', 'array.filter(foo).at(-1)', - ], + ].map(code => ({code, options: [{checkFromLast: false}]})), invalid: [], }); @@ -968,7 +965,7 @@ test({ 'array.filter().pop()', 'array.filter(foo, thisArgument, extraArgument).pop()', 'array.filter(...foo).pop()', - ].map(code => ({code, options: checkFromLastOptions})), + ], invalid: [ { code: 'array.filter(foo).pop()', @@ -1005,7 +1002,7 @@ test({ `, errors: [{messageId: ERROR_POP}], }, - ].map(test => ({...test, options: checkFromLastOptions})), + ], }); // `.at(-1)` @@ -1058,7 +1055,7 @@ test({ 'array.filter().at(-1)', 'array.filter(foo, thisArgument, extraArgument).at(-1)', 'array.filter(...foo).at(-1)', - ].map(code => ({code, options: checkFromLastOptions})), + ], invalid: [ { code: 'array.filter(foo).at(-1)', @@ -1099,7 +1096,7 @@ test({ `, errors: [{messageId: ERROR_AT_MINUS_ONE}], }, - ].map(test => ({...test, options: checkFromLastOptions})), + ], }); // `.at(0)` diff --git a/test/prefer-array-some.mjs b/test/prefer-array-some.mjs index 8dadeabcd5..181cbf5143 100644 --- a/test/prefer-array-some.mjs +++ b/test/prefer-array-some.mjs @@ -210,6 +210,40 @@ test.snapshot({ ], }); +// `.{findIndex,findLastIndex}(…) !== -1` +// `.{findIndex,findLastIndex}(…) != -1` +// `.{findIndex,findLastIndex}(…) > -1` +// `.{findIndex,findLastIndex}(…) === -1` +// `.{findIndex,findLastIndex}(…) == -1` +// `.{findIndex,findLastIndex}(…) >= 0` +// `.{findIndex,findLastIndex}(…) < 0` +test.snapshot({ + valid: [ + 'foo.notMatchedMethod(bar) !== -1', + 'new foo.findIndex(bar) !== -1', + 'foo.findIndex(bar, extraArgument) !== -1', + 'foo.findIndex(bar) instanceof -1', + 'foo.findIndex(...bar) !== -1', + // We are not ignoring ``{_,lodash,underscore}.{findIndex,findLastIndex}` + // but it doesn't make sense to use them with one argument + '_.findIndex(bar)', + '_.findIndex(foo, bar)', + ], + invalid: [ + ...[ + 'foo.findIndex(bar) !== -1', + 'foo.findIndex(bar) != -1', + 'foo.findIndex(bar) > - 1', + 'foo.findIndex(bar) === -1', + 'foo.findIndex(bar) == - 1', + 'foo.findIndex(bar) >= 0', + 'foo.findIndex(bar) < 0', + ].flatMap(code => [code, code.replace('findIndex', 'findLastIndex')]), + 'foo.findIndex(bar) !== (( - 1 ))', + 'foo.findIndex(element => element.bar === 1) !== (( - 1 ))', + ], +}); + test.vue({ valid: [], invalid: [ diff --git a/test/prefer-includes.mjs b/test/prefer-includes.mjs index 0ea72fd7fe..963d87149e 100644 --- a/test/prefer-includes.mjs +++ b/test/prefer-includes.mjs @@ -5,20 +5,22 @@ const {test} = getTester(import.meta); test.snapshot({ valid: [ - 'str.indexOf(\'foo\') !== -n', - 'str.indexOf(\'foo\') !== 1', - 'str.indexOf(\'foo\') === -2', - '!str.indexOf(\'foo\') === 1', - '!str.indexOf(\'foo\') === -n', + ...[ + 'str.indexOf(\'foo\') !== -n', + 'str.indexOf(\'foo\') !== 1', + 'str.indexOf(\'foo\') === -2', + '!str.indexOf(\'foo\') === 1', + '!str.indexOf(\'foo\') === -n', + 'null.indexOf(\'foo\') !== 1', + 'something.indexOf(foo, 0, another) !== -1', + '_.indexOf(foo, bar) !== -1', + 'lodash.indexOf(foo, bar) !== -1', + 'underscore.indexOf(foo, bar) !== -1', + ].flatMap(code => [code, code.replace('.indexOf', '.lastIndexOf')]), 'str.includes(\'foo\')', '\'foobar\'.includes(\'foo\')', '[1,2,3].includes(4)', - 'null.indexOf(\'foo\') !== 1', 'f(0) < 0', - 'something.indexOf(foo, 0, another) !== -1', - '_.indexOf(foo, bar) !== -1', - 'lodash.indexOf(foo, bar) !== -1', - 'underscore.indexOf(foo, bar) !== -1', ], invalid: [ '\'foobar\'.indexOf(\'foo\') !== -1', @@ -32,7 +34,7 @@ test.snapshot({ '(a || b).indexOf(\'foo\') === -1', 'foo.indexOf(bar, 0) !== -1', 'foo.indexOf(bar, 1) !== -1', - ], + ].flatMap(code => [code, code.replace('.indexOf', '.lastIndexOf')]), }); const {snapshot, typescript} = tests({ diff --git a/test/prefer-string-raw.mjs b/test/prefer-string-raw.mjs index 2f818eac66..af5e034c7a 100644 --- a/test/prefer-string-raw.mjs +++ b/test/prefer-string-raw.mjs @@ -38,3 +38,19 @@ test.snapshot({ String.raw`const foo = "foo \\x46";`, ], }); + +test.typescript({ + valid: [ + outdent` + enum Files { + Foo = "C:\\\\path\\\\to\\\\foo.js", + } + `, + outdent` + enum Foo { + "\\\\a\\\\b" = "baz", + } + `, + ], + invalid: [], +}); diff --git a/test/run-rules-on-codebase/lint.mjs b/test/run-rules-on-codebase/lint.mjs index 098641b8ab..bf72648400 100644 --- a/test/run-rules-on-codebase/lint.mjs +++ b/test/run-rules-on-codebase/lint.mjs @@ -23,12 +23,8 @@ const { }); const configs = [ - // TODO: Use `eslintPluginUnicorn.configs.all` instead when we change preset to flat config + eslintPluginUnicorn.configs['flat/all'], { - plugins: { - unicorn: eslintPluginUnicorn, - }, - rules: eslintPluginUnicorn.configs.all.rules, linterOptions: { reportUnusedDisableDirectives: false, }, @@ -38,8 +34,6 @@ const configs = [ 'coverage', 'test/integration/fixtures', 'test/integration/fixtures-local', - // Ignore this file self temporarily, disabling `n/file-extension-in-import` comment cause error - 'test/run-rules-on-codebase/lint.mjs', 'rules/utils/lodash.js', ], }, @@ -55,10 +49,6 @@ const configs = [ 'unicorn/consistent-function-scoping': 'off', // Annoying 'unicorn/no-keyword-prefix': 'off', - 'unicorn/no-unsafe-regex': 'off', - // Not ready yet - 'unicorn/prefer-string-replace-all': 'off', - 'unicorn/prefer-at': 'off', }, }, { diff --git a/test/smoke/eslint-remote-tester.config.js b/test/smoke/eslint-remote-tester.config.js deleted file mode 100644 index 7dbfba9971..0000000000 --- a/test/smoke/eslint-remote-tester.config.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -const {getRepositories, getPathIgnorePattern} = require('eslint-remote-tester-repositories'); - -module.exports = { - /** Repositories to scan */ - repositories: getRepositories({randomize: true}), - - /** Optional pattern used to exclude paths */ - pathIgnorePattern: getPathIgnorePattern(), - - /** Extensions of files under scanning */ - extensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'vue'], - - /** Maximum amount of tasks ran concurrently */ - concurrentTasks: 3, - - /** Optional boolean flag used to enable caching of cloned repositories. For CIs it's ideal to disable caching. Defaults to true. */ - cache: false, - - /** Optional setting for log level. Valid values are verbose, info, warn, error. Defaults to verbose. */ - logLevel: 'info', - - /** ESLint configuration */ - eslintrc: { - root: true, - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - project: [], - }, - extends: ['plugin:unicorn/all'], - }, -}; diff --git a/test/smoke/eslint-remote-tester.config.mjs b/test/smoke/eslint-remote-tester.config.mjs new file mode 100644 index 0000000000..f9ca31aec0 --- /dev/null +++ b/test/smoke/eslint-remote-tester.config.mjs @@ -0,0 +1,63 @@ +import { + getRepositories, + getPathIgnorePattern, +} from 'eslint-remote-tester-repositories'; +import typescriptParser from '@typescript-eslint/parser'; +import vueParser from 'vue-eslint-parser'; +import eslintPluginUnicorn from '../../index.js'; + +/** @type {import('eslint-remote-tester').Config} */ +const config = { + /** Repositories to scan */ + repositories: getRepositories({randomize: true}), + + /** Optional pattern used to exclude paths */ + pathIgnorePattern: getPathIgnorePattern(), + + /** Extensions of files under scanning */ + extensions: ['js', 'cjs', 'mjs', 'ts', 'cts', 'mts', 'jsx', 'tsx', 'vue'], + + /** Maximum amount of tasks ran concurrently */ + concurrentTasks: 3, + + /** Optional boolean flag used to enable caching of cloned repositories. For CIs it's ideal to disable caching. Defaults to true. */ + cache: false, + + /** Optional setting for log level. Valid values are verbose, info, warn, error. Defaults to verbose. */ + logLevel: 'info', + + /** ESLint configuration */ + eslintConfig: [ + eslintPluginUnicorn.configs['flat/all'], + { + rules: { + // This rule crashing on replace string inside `jsx` or `Unicode escape sequence` + 'unicorn/string-content': 'off', + }, + }, + { + files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.tsx'], + languageOptions: { + parser: typescriptParser, + parserOptions: { + project: [], + }, + }, + }, + { + files: ['**/*.vue'], + languageOptions: { + parser: vueParser, + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaFeatures: { + jsx: true, + }, + project: [], + }, + }, + }, + ], +}; + +export default config; diff --git a/test/snapshots/new-for-builtins.mjs.md b/test/snapshots/new-for-builtins.mjs.md index df6ce0dce8..196f2cb928 100644 --- a/test/snapshots/new-for-builtins.mjs.md +++ b/test/snapshots/new-for-builtins.mjs.md @@ -1267,105 +1267,124 @@ Generated by [AVA](https://avajs.dev). > Input `␊ - 1 |␊ - 2 | function varCheck() {␊ - 3 | {␊ - 4 | var WeakMap = function() {};␊ - 5 | }␊ - 6 | // This should not reported␊ - 7 | return WeakMap()␊ - 8 | }␊ - 9 | function constCheck() {␊ - 10 | {␊ - 11 | const Array = function() {};␊ - 12 | }␊ - 13 | return Array()␊ - 14 | }␊ - 15 | function letCheck() {␊ - 16 | {␊ - 17 | let Map = function() {};␊ - 18 | }␊ - 19 | return Map()␊ - 20 | }␊ - 21 | ␊ + 1 | function varCheck() {␊ + 2 | {␊ + 3 | var WeakMap = function() {};␊ + 4 | }␊ + 5 | // This should not reported␊ + 6 | return WeakMap()␊ + 7 | }␊ + 8 | function constCheck() {␊ + 9 | {␊ + 10 | const Array = function() {};␊ + 11 | }␊ + 12 | return Array()␊ + 13 | }␊ + 14 | function letCheck() {␊ + 15 | {␊ + 16 | let Map = function() {};␊ + 17 | }␊ + 18 | return Map()␊ + 19 | }␊ ` > Output `␊ - 1 |␊ - 2 | function varCheck() {␊ - 3 | {␊ - 4 | var WeakMap = function() {};␊ - 5 | }␊ - 6 | // This should not reported␊ - 7 | return WeakMap()␊ - 8 | }␊ - 9 | function constCheck() {␊ - 10 | {␊ - 11 | const Array = function() {};␊ - 12 | }␊ - 13 | return new Array()␊ - 14 | }␊ - 15 | function letCheck() {␊ - 16 | {␊ - 17 | let Map = function() {};␊ - 18 | }␊ - 19 | return new Map()␊ - 20 | }␊ - 21 | ␊ + 1 | function varCheck() {␊ + 2 | {␊ + 3 | var WeakMap = function() {};␊ + 4 | }␊ + 5 | // This should not reported␊ + 6 | return WeakMap()␊ + 7 | }␊ + 8 | function constCheck() {␊ + 9 | {␊ + 10 | const Array = function() {};␊ + 11 | }␊ + 12 | return new Array()␊ + 13 | }␊ + 14 | function letCheck() {␊ + 15 | {␊ + 16 | let Map = function() {};␊ + 17 | }␊ + 18 | return new Map()␊ + 19 | }␊ ` > Error 1/2 `␊ - 1 |␊ - 2 | function varCheck() {␊ - 3 | {␊ - 4 | var WeakMap = function() {};␊ - 5 | }␊ - 6 | // This should not reported␊ - 7 | return WeakMap()␊ - 8 | }␊ - 9 | function constCheck() {␊ - 10 | {␊ - 11 | const Array = function() {};␊ - 12 | }␊ - > 13 | return Array()␊ - | ^^^^^^^ Use \`new Array()\` instead of \`Array()\`.␊ - 14 | }␊ - 15 | function letCheck() {␊ - 16 | {␊ - 17 | let Map = function() {};␊ - 18 | }␊ - 19 | return Map()␊ - 20 | }␊ - 21 | ␊ + 1 | function varCheck() {␊ + 2 | {␊ + 3 | var WeakMap = function() {};␊ + 4 | }␊ + 5 | // This should not reported␊ + 6 | return WeakMap()␊ + 7 | }␊ + 8 | function constCheck() {␊ + 9 | {␊ + 10 | const Array = function() {};␊ + 11 | }␊ + > 12 | return Array()␊ + | ^^^^^^^ Use \`new Array()\` instead of \`Array()\`.␊ + 13 | }␊ + 14 | function letCheck() {␊ + 15 | {␊ + 16 | let Map = function() {};␊ + 17 | }␊ + 18 | return Map()␊ + 19 | }␊ ` > Error 2/2 `␊ - 1 |␊ - 2 | function varCheck() {␊ - 3 | {␊ - 4 | var WeakMap = function() {};␊ - 5 | }␊ - 6 | // This should not reported␊ - 7 | return WeakMap()␊ - 8 | }␊ - 9 | function constCheck() {␊ - 10 | {␊ - 11 | const Array = function() {};␊ - 12 | }␊ - 13 | return Array()␊ - 14 | }␊ - 15 | function letCheck() {␊ - 16 | {␊ - 17 | let Map = function() {};␊ - 18 | }␊ - > 19 | return Map()␊ - | ^^^^^ Use \`new Map()\` instead of \`Map()\`.␊ - 20 | }␊ - 21 | ␊ + 1 | function varCheck() {␊ + 2 | {␊ + 3 | var WeakMap = function() {};␊ + 4 | }␊ + 5 | // This should not reported␊ + 6 | return WeakMap()␊ + 7 | }␊ + 8 | function constCheck() {␊ + 9 | {␊ + 10 | const Array = function() {};␊ + 11 | }␊ + 12 | return Array()␊ + 13 | }␊ + 14 | function letCheck() {␊ + 15 | {␊ + 16 | let Map = function() {};␊ + 17 | }␊ + > 18 | return Map()␊ + | ^^^^^ Use \`new Map()\` instead of \`Map()\`.␊ + 19 | }␊ + ` + +## invalid(60): function foo() { return(globalThis).Map() } + +> Input + + `␊ + 1 | function foo() {␊ + 2 | return(globalThis).Map()␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function foo() {␊ + 2 | return new (globalThis).Map()␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function foo() {␊ + > 2 | return(globalThis).Map()␊ + | ^^^^^^^^^^^^^^^^^^ Use \`new Map()\` instead of \`Map()\`.␊ + 3 | }␊ ` diff --git a/test/snapshots/new-for-builtins.mjs.snap b/test/snapshots/new-for-builtins.mjs.snap index 51412c8f5e..c47a5dd2d2 100644 Binary files a/test/snapshots/new-for-builtins.mjs.snap and b/test/snapshots/new-for-builtins.mjs.snap differ diff --git a/test/snapshots/no-negation-in-equality-check.mjs.md b/test/snapshots/no-negation-in-equality-check.mjs.md new file mode 100644 index 0000000000..6888103721 --- /dev/null +++ b/test/snapshots/no-negation-in-equality-check.mjs.md @@ -0,0 +1,268 @@ +# Snapshot report for `test/no-negation-in-equality-check.mjs` + +The actual snapshot is saved in `no-negation-in-equality-check.mjs.snap`. + +Generated by [AVA](https://avajs.dev). + +## invalid(1): !foo === bar + +> Input + + `␊ + 1 | !foo === bar␊ + ` + +> Error 1/1 + + `␊ + > 1 | !foo === bar␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | foo !== bar␊ + ` + +## invalid(2): !foo !== bar + +> Input + + `␊ + 1 | !foo !== bar␊ + ` + +> Error 1/1 + + `␊ + > 1 | !foo !== bar␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '===' check.␊ + 1 | foo === bar␊ + ` + +## invalid(3): !foo == bar + +> Input + + `␊ + 1 | !foo == bar␊ + ` + +> Error 1/1 + + `␊ + > 1 | !foo == bar␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!=' check.␊ + 1 | foo != bar␊ + ` + +## invalid(4): !foo != bar + +> Input + + `␊ + 1 | !foo != bar␊ + ` + +> Error 1/1 + + `␊ + > 1 | !foo != bar␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '==' check.␊ + 1 | foo == bar␊ + ` + +## invalid(5): function x() { return!foo === bar; } + +> Input + + `␊ + 1 | function x() {␊ + 2 | return!foo === bar;␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function x() {␊ + > 2 | return!foo === bar;␊ + | ^ Negated expression in not allowed in equality check.␊ + 3 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | function x() {␊ + 2 | return foo !== bar;␊ + 3 | }␊ + ` + +## invalid(6): function x() { return! foo === bar; throw! foo === bar; } + +> Input + + `␊ + 1 | function x() {␊ + 2 | return!␊ + 3 | foo === bar;␊ + 4 | throw!␊ + 5 | foo === bar;␊ + 6 | }␊ + ` + +> Error 1/2 + + `␊ + 1 | function x() {␊ + > 2 | return!␊ + | ^ Negated expression in not allowed in equality check.␊ + 3 | foo === bar;␊ + 4 | throw!␊ + 5 | foo === bar;␊ + 6 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | function x() {␊ + 2 | return (␊ + 3 | foo !== bar);␊ + 4 | throw!␊ + 5 | foo === bar;␊ + 6 | }␊ + ` + +> Error 2/2 + + `␊ + 1 | function x() {␊ + 2 | return!␊ + 3 | foo === bar;␊ + > 4 | throw!␊ + | ^ Negated expression in not allowed in equality check.␊ + 5 | foo === bar;␊ + 6 | }␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | function x() {␊ + 2 | return!␊ + 3 | foo === bar;␊ + 4 | throw (␊ + 5 | foo !== bar);␊ + 6 | }␊ + ` + +## invalid(7): foo !(a) === b + +> Input + + `␊ + 1 | foo␊ + 2 | !(a) === b␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | !(a) === b␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | foo␊ + 2 | ;(a) !== b␊ + ` + +## invalid(8): foo ![a, b].join('') === c + +> Input + + `␊ + 1 | foo␊ + 2 | ![a, b].join('') === c␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | ![a, b].join('') === c␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | foo␊ + 2 | ;[a, b].join('') !== c␊ + ` + +## invalid(9): foo ! [a, b].join('') === c + +> Input + + `␊ + 1 | foo␊ + 2 | ! [a, b].join('') === c␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | ! [a, b].join('') === c␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | foo␊ + 2 | ; [a, b].join('') !== c␊ + ` + +## invalid(10): foo !/* comment */[a, b].join('') === c + +> Input + + `␊ + 1 | foo␊ + 2 | !/* comment */[a, b].join('') === c␊ + ` + +> Error 1/1 + + `␊ + 1 | foo␊ + > 2 | !/* comment */[a, b].join('') === c␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | foo␊ + 2 | ;/* comment */[a, b].join('') !== c␊ + ` + +## invalid(11): !!foo === bar + +> Input + + `␊ + 1 | !!foo === bar␊ + ` + +> Error 1/1 + + `␊ + > 1 | !!foo === bar␊ + | ^ Negated expression in not allowed in equality check.␊ + ␊ + --------------------------------------------------------------------------------␊ + Suggestion 1/1: Switch to '!==' check.␊ + 1 | !foo !== bar␊ + ` diff --git a/test/snapshots/no-negation-in-equality-check.mjs.snap b/test/snapshots/no-negation-in-equality-check.mjs.snap new file mode 100644 index 0000000000..9df853b5dd Binary files /dev/null and b/test/snapshots/no-negation-in-equality-check.mjs.snap differ diff --git a/test/snapshots/prefer-array-some.mjs.md b/test/snapshots/prefer-array-some.mjs.md index 4bc92a86e3..d0a5d9a363 100644 --- a/test/snapshots/prefer-array-some.mjs.md +++ b/test/snapshots/prefer-array-some.mjs.md @@ -184,6 +184,342 @@ Generated by [AVA](https://avajs.dev). 15 | );␊ ` +## invalid(1): foo.findIndex(bar) !== -1 + +> Input + + `␊ + 1 | foo.findIndex(bar) !== -1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) !== -1␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(2): foo.findLastIndex(bar) !== -1 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) !== -1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) !== -1␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(3): foo.findIndex(bar) != -1 + +> Input + + `␊ + 1 | foo.findIndex(bar) != -1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) != -1␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(4): foo.findLastIndex(bar) != -1 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) != -1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) != -1␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(5): foo.findIndex(bar) > - 1 + +> Input + + `␊ + 1 | foo.findIndex(bar) > - 1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) > - 1␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(6): foo.findLastIndex(bar) > - 1 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) > - 1␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) > - 1␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(7): foo.findIndex(bar) === -1 + +> Input + + `␊ + 1 | foo.findIndex(bar) === -1␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) === -1␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(8): foo.findLastIndex(bar) === -1 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) === -1␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) === -1␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(9): foo.findIndex(bar) == - 1 + +> Input + + `␊ + 1 | foo.findIndex(bar) == - 1␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) == - 1␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(10): foo.findLastIndex(bar) == - 1 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) == - 1␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) == - 1␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(11): foo.findIndex(bar) >= 0 + +> Input + + `␊ + 1 | foo.findIndex(bar) >= 0␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) >= 0␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(12): foo.findLastIndex(bar) >= 0 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) >= 0␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) >= 0␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(13): foo.findIndex(bar) < 0 + +> Input + + `␊ + 1 | foo.findIndex(bar) < 0␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) < 0␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(14): foo.findLastIndex(bar) < 0 + +> Input + + `␊ + 1 | foo.findLastIndex(bar) < 0␊ + ` + +> Output + + `␊ + 1 | !foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findLastIndex(bar) < 0␊ + | ^^^^^^^^^^^^^ Prefer \`.some(…)\` over \`.findLastIndex(…)\`.␊ + ` + +## invalid(15): foo.findIndex(bar) !== (( - 1 )) + +> Input + + `␊ + 1 | foo.findIndex(bar) !== (( - 1 ))␊ + ` + +> Output + + `␊ + 1 | foo.some(bar) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(bar) !== (( - 1 ))␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + +## invalid(16): foo.findIndex(element => element.bar === 1) !== (( - 1 )) + +> Input + + `␊ + 1 | foo.findIndex(element => element.bar === 1) !== (( - 1 ))␊ + ` + +> Output + + `␊ + 1 | foo.some(element => element.bar === 1) ␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.findIndex(element => element.bar === 1) !== (( - 1 ))␊ + | ^^^^^^^^^ Prefer \`.some(…)\` over \`.findIndex(…)\`.␊ + ` + ## invalid(1): foo.find(fn) == null > Input diff --git a/test/snapshots/prefer-array-some.mjs.snap b/test/snapshots/prefer-array-some.mjs.snap index 67bff2893c..a7086be00a 100644 Binary files a/test/snapshots/prefer-array-some.mjs.snap and b/test/snapshots/prefer-array-some.mjs.snap differ diff --git a/test/snapshots/prefer-includes.mjs.md b/test/snapshots/prefer-includes.mjs.md index 92011629b7..97207ce93e 100644 --- a/test/snapshots/prefer-includes.mjs.md +++ b/test/snapshots/prefer-includes.mjs.md @@ -25,7 +25,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(2): str.indexOf('foo') != -1 +## invalid(2): 'foobar'.lastIndexOf('foo') !== -1 + +> Input + + `␊ + 1 | 'foobar'.lastIndexOf('foo') !== -1␊ + ` + +> Output + + `␊ + 1 | 'foobar'.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | 'foobar'.lastIndexOf('foo') !== -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(3): str.indexOf('foo') != -1 > Input @@ -46,7 +67,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(3): str.indexOf('foo') > -1 +## invalid(4): str.lastIndexOf('foo') != -1 + +> Input + + `␊ + 1 | str.lastIndexOf('foo') != -1␊ + ` + +> Output + + `␊ + 1 | str.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.lastIndexOf('foo') != -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(5): str.indexOf('foo') > -1 > Input @@ -67,7 +109,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(4): str.indexOf('foo') == -1 +## invalid(6): str.lastIndexOf('foo') > -1 + +> Input + + `␊ + 1 | str.lastIndexOf('foo') > -1␊ + ` + +> Output + + `␊ + 1 | str.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.lastIndexOf('foo') > -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(7): str.indexOf('foo') == -1 > Input @@ -88,7 +151,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(5): 'foobar'.indexOf('foo') >= 0 +## invalid(8): str.lastIndexOf('foo') == -1 + +> Input + + `␊ + 1 | str.lastIndexOf('foo') == -1␊ + ` + +> Output + + `␊ + 1 | !str.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.lastIndexOf('foo') == -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(9): 'foobar'.indexOf('foo') >= 0 > Input @@ -109,7 +193,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(6): [1,2,3].indexOf(4) !== -1 +## invalid(10): 'foobar'.lastIndexOf('foo') >= 0 + +> Input + + `␊ + 1 | 'foobar'.lastIndexOf('foo') >= 0␊ + ` + +> Output + + `␊ + 1 | 'foobar'.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | 'foobar'.lastIndexOf('foo') >= 0␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(11): [1,2,3].indexOf(4) !== -1 > Input @@ -130,7 +235,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(7): str.indexOf('foo') < 0 +## invalid(12): [1,2,3].lastIndexOf(4) !== -1 + +> Input + + `␊ + 1 | [1,2,3].lastIndexOf(4) !== -1␊ + ` + +> Output + + `␊ + 1 | [1,2,3].includes(4)␊ + ` + +> Error 1/1 + + `␊ + > 1 | [1,2,3].lastIndexOf(4) !== -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(13): str.indexOf('foo') < 0 > Input @@ -151,7 +277,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(8): ''.indexOf('foo') < 0 +## invalid(14): str.lastIndexOf('foo') < 0 + +> Input + + `␊ + 1 | str.lastIndexOf('foo') < 0␊ + ` + +> Output + + `␊ + 1 | !str.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | str.lastIndexOf('foo') < 0␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(15): ''.indexOf('foo') < 0 > Input @@ -172,7 +319,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(9): (a || b).indexOf('foo') === -1 +## invalid(16): ''.lastIndexOf('foo') < 0 + +> Input + + `␊ + 1 | ''.lastIndexOf('foo') < 0␊ + ` + +> Output + + `␊ + 1 | !''.includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | ''.lastIndexOf('foo') < 0␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(17): (a || b).indexOf('foo') === -1 > Input @@ -193,7 +361,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(10): foo.indexOf(bar, 0) !== -1 +## invalid(18): (a || b).lastIndexOf('foo') === -1 + +> Input + + `␊ + 1 | (a || b).lastIndexOf('foo') === -1␊ + ` + +> Output + + `␊ + 1 | !(a || b).includes('foo')␊ + ` + +> Error 1/1 + + `␊ + > 1 | (a || b).lastIndexOf('foo') === -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(19): foo.indexOf(bar, 0) !== -1 > Input @@ -214,7 +403,28 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` -## invalid(11): foo.indexOf(bar, 1) !== -1 +## invalid(20): foo.lastIndexOf(bar, 0) !== -1 + +> Input + + `␊ + 1 | foo.lastIndexOf(bar, 0) !== -1␊ + ` + +> Output + + `␊ + 1 | foo.includes(bar)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.lastIndexOf(bar, 0) !== -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + +## invalid(21): foo.indexOf(bar, 1) !== -1 > Input @@ -235,6 +445,27 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^ Use \`.includes()\`, rather than \`.indexOf()\`, when checking for existence.␊ ` +## invalid(22): foo.lastIndexOf(bar, 1) !== -1 + +> Input + + `␊ + 1 | foo.lastIndexOf(bar, 1) !== -1␊ + ` + +> Output + + `␊ + 1 | foo.includes(bar, 1)␊ + ` + +> Error 1/1 + + `␊ + > 1 | foo.lastIndexOf(bar, 1) !== -1␊ + | ^^^^^^^^^^^ Use \`.includes()\`, rather than \`.lastIndexOf()\`, when checking for existence.␊ + ` + ## invalid(1): values.some(x => x === "foo") > Input diff --git a/test/snapshots/prefer-includes.mjs.snap b/test/snapshots/prefer-includes.mjs.snap index dd84e89630..b5991d9658 100644 Binary files a/test/snapshots/prefer-includes.mjs.snap and b/test/snapshots/prefer-includes.mjs.snap differ diff --git a/test/snapshots/throw-new-error.mjs.md b/test/snapshots/throw-new-error.mjs.md index 8e36df5d8e..903b034a02 100644 --- a/test/snapshots/throw-new-error.mjs.md +++ b/test/snapshots/throw-new-error.mjs.md @@ -534,3 +534,30 @@ Generated by [AVA](https://avajs.dev). | ^^^^^^^^^^^^^^^^ Use \`new\` when creating an error.␊ 3 | });␊ ` + +## invalid(26): function foo() { return[globalThis][0].Error('message'); } + +> Input + + `␊ + 1 | function foo() {␊ + 2 | return[globalThis][0].Error('message');␊ + 3 | }␊ + ` + +> Output + + `␊ + 1 | function foo() {␊ + 2 | return new [globalThis][0].Error('message');␊ + 3 | }␊ + ` + +> Error 1/1 + + `␊ + 1 | function foo() {␊ + > 2 | return[globalThis][0].Error('message');␊ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`new\` when creating an error.␊ + 3 | }␊ + ` diff --git a/test/snapshots/throw-new-error.mjs.snap b/test/snapshots/throw-new-error.mjs.snap index aa2b8ade04..84e6045ed9 100644 Binary files a/test/snapshots/throw-new-error.mjs.snap and b/test/snapshots/throw-new-error.mjs.snap differ diff --git a/test/throw-new-error.mjs b/test/throw-new-error.mjs index e240b19e77..7019c57ca4 100644 --- a/test/throw-new-error.mjs +++ b/test/throw-new-error.mjs @@ -60,5 +60,10 @@ test.snapshot({ reject(Error('message')); }); `, + outdent` + function foo() { + return[globalThis][0].Error('message'); + } + `, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy