diff --git a/docs/rules/prefer-trim-start-end.md b/docs/rules/prefer-trim-start-end.md new file mode 100644 index 0000000000..661d9fbe1d --- /dev/null +++ b/docs/rules/prefer-trim-start-end.md @@ -0,0 +1,19 @@ +# Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()` + +[`String#trimLeft()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimLeft) and [`String#trimRight()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimRight) are aliases of [`String#trimStart()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart) and [`String#trimEnd()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd) + +This rule is fixable. + +## Fail + +```js +const foo = bar.trimStart(); +const foo = bar.trimEnd(); +``` + +## Pass + +```js +const foo = bar.trimLeft(); +const foo = bar.trimRight(); +``` diff --git a/index.js b/index.js index b03af46c77..5c576ff581 100644 --- a/index.js +++ b/index.js @@ -57,6 +57,7 @@ module.exports = { 'unicorn/prefer-starts-ends-with': 'error', 'unicorn/prefer-string-slice': 'error', 'unicorn/prefer-text-content': 'error', + 'unicorn/prefer-trim-start-end': 'error', 'unicorn/prefer-type-error': 'error', 'unicorn/prevent-abbreviations': 'error', 'unicorn/regex-shorthand': 'error', diff --git a/readme.md b/readme.md index 2da255ac21..f40c6e4753 100644 --- a/readme.md +++ b/readme.md @@ -75,6 +75,7 @@ Configure it in `package.json`. "unicorn/prefer-starts-ends-with": "error", "unicorn/prefer-string-slice": "error", "unicorn/prefer-text-content": "error", + "unicorn/prefer-trim-start-end": "error", "unicorn/prefer-type-error": "error", "unicorn/prevent-abbreviations": "error", "unicorn/regex-shorthand": "error", @@ -126,6 +127,7 @@ Configure it in `package.json`. - [prefer-starts-ends-with](docs/rules/prefer-starts-ends-with.md) - Prefer `String#startsWith()` & `String#endsWith()` over more complex alternatives. - [prefer-string-slice](docs/rules/prefer-string-slice.md) - Prefer `String#slice()` over `String#substr()` and `String#substring()`. *(partly fixable)* - [prefer-text-content](docs/rules/prefer-text-content.md) - Prefer `.textContent` over `.innerText`. *(fixable)* +- [prefer-trim-start-end](docs/rules/prefer-trim-start-end.md) - Prefer `String#trimStart()` / `String#trimEnd()` over `String#trimLeft()` / `String#trimRight()`. *(fixable)* - [prefer-type-error](docs/rules/prefer-type-error.md) - Enforce throwing `TypeError` in type checking conditions. *(fixable)* - [prevent-abbreviations](docs/rules/prevent-abbreviations.md) - Prevent abbreviations. *(partly fixable)* - [regex-shorthand](docs/rules/regex-shorthand.md) - Enforce the use of regex shorthands to improve readability. *(fixable)* diff --git a/rules/prefer-trim-start-end.js b/rules/prefer-trim-start-end.js new file mode 100644 index 0000000000..fe7338601f --- /dev/null +++ b/rules/prefer-trim-start-end.js @@ -0,0 +1,55 @@ +'use strict'; +const getDocumentationUrl = require('./utils/get-documentation-url'); + +const methods = new Map([ + ['trimLeft', 'trimStart'], + ['trimRight', 'trimEnd'] +]); + +const messages = {}; + +for (const [method, replacement] of methods.entries()) { + messages[method] = `Prefer \`String#${method}()\` over \`String#${replacement}()\`.`; +} + +const create = context => { + return { + CallExpression(node) { + const {callee, arguments: arguments_} = node; + + if ( + callee.type !== 'MemberExpression' || + callee.property.type !== 'Identifier' || + arguments_.length !== 0 + ) { + return; + } + + const method = callee.property.name; + + if (!methods.has(method)) { + return; + } + + const replacement = methods.get(method); + + context.report({ + node, + messageId: method, + fix: fixer => fixer.replaceText(callee.property, replacement) + }); + } + }; +}; + +module.exports = { + create, + meta: { + type: 'suggestion', + docs: { + url: getDocumentationUrl(__filename) + }, + fixable: 'code', + messages + } +}; diff --git a/test/prefer-trim-start-end.js b/test/prefer-trim-start-end.js new file mode 100644 index 0000000000..b60b2f0842 --- /dev/null +++ b/test/prefer-trim-start-end.js @@ -0,0 +1,51 @@ +import test from 'ava'; +import avaRuleTester from 'eslint-ava-rule-tester'; +import rule from '../rules/prefer-trim-start-end'; + +const ruleTester = avaRuleTester(test, { + env: { + es6: true + } +}); + +const errorTrimLeft = { + ruleId: 'prefer-trim-start-end', + messageId: 'trimLeft' +}; + +const errorTrimRight = { + ruleId: 'prefer-trim-start-end', + messageId: 'trimRight' +}; + +ruleTester.run('prefer-flat-map', rule, { + valid: [ + 'foo.trimStart()', + 'foo.trimEnd()', + // Extra arguments + 'foo.trimLeft(1)', + // New + 'new foo.trimLeft()', + // Function call + 'trimLeft()', + // Not call + 'foo.trimLeft' + ], + invalid: [ + { + code: 'foo.trimLeft()', + output: 'foo.trimStart()', + errors: [errorTrimLeft] + }, + { + code: 'foo.trimRight()', + output: 'foo.trimEnd()', + errors: [errorTrimRight] + }, + { + code: '"foo".trimLeft()', + output: '"foo".trimStart()', + errors: [errorTrimLeft] + } + ] +});