From 200209363cd42438b1e4b64cd51b8b05c5380537 Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Tue, 26 May 2020 23:54:39 -0500 Subject: [PATCH] Use singular array element variable name in autofix for `no-for-loop` rule (#745) --- package.json | 1 + rules/no-for-loop.js | 10 ++++++++- test/no-for-loop.js | 51 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dea370eded..fd51fde919 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "eslint-utils": "^2.0.0", "import-modules": "^2.0.0", "lodash": "^4.17.15", + "pluralize": "^8.0.0", "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.21", "reserved-words": "^0.1.2", diff --git a/rules/no-for-loop.js b/rules/no-for-loop.js index e2f40885de..5d8803f8fe 100644 --- a/rules/no-for-loop.js +++ b/rules/no-for-loop.js @@ -3,6 +3,7 @@ const getDocumentationUrl = require('./utils/get-documentation-url'); const isLiteralValue = require('./utils/is-literal-value'); const {flatten} = require('lodash'); const avoidCapture = require('./utils/avoid-capture'); +const {singular} = require('pluralize'); const defaultElementName = 'element'; const isLiteralZero = node => isLiteralValue(node, 0); @@ -267,6 +268,13 @@ const getChildScopesRecursive = scope => [ ...flatten(scope.childScopes.map(scope => getChildScopesRecursive(scope))) ]; +const getSingularName = originalName => { + const singularName = singular(originalName); + if (singularName !== originalName) { + return singularName; + } +}; + const create = context => { const sourceCode = context.getSourceCode(); const {scopeManager} = sourceCode; @@ -342,7 +350,7 @@ const create = context => { const index = indexIdentifierName; const element = elementIdentifierName || - avoidCapture(defaultElementName, getChildScopesRecursive(bodyScope), context.parserOptions.ecmaVersion); + avoidCapture(getSingularName(arrayIdentifierName) || defaultElementName, getChildScopesRecursive(bodyScope), context.parserOptions.ecmaVersion); const array = arrayIdentifierName; let declarationElement = element; diff --git a/test/no-for-loop.js b/test/no-for-loop.js index 43fbf888d6..2ef400aa4d 100644 --- a/test/no-for-loop.js +++ b/test/no-for-loop.js @@ -649,6 +649,57 @@ ruleTester.run('no-for-loop', rule, { console.log(element); console.log(element_); } + `), + + // Singularization: + ...[ + ['plugin', 'plugins'], // Simple + ['person', 'people'], // Irregular + ['girlsAndBoy', 'girlsAndBoys'], // Multiple plurals + ['largeCity', 'largeCities'], // CamelCase + ['LARGE_CITY', 'LARGE_CITIES'], // Caps, snake_case + ['element', 'news'], // No singular version, ends in s + ['element', 'list'] // No singular version + ].map(([elementName, arrayName]) => + testCase( + `for(const i = 0; i < ${arrayName}.length; i++) {console.log(${arrayName}[i])}`, + `for(const ${elementName} of ${arrayName}) {console.log(${elementName})}` + ) + ), + + // Singularization (avoid using reserved JavaScript keywords): + testCase(outdent` + for (let i = 0; i < cases.length; i++) { + console.log(cases[i]); + } + `, outdent` + for (const case_ of cases) { + console.log(case_); + } + `), + // Singularization (avoid variable name collision): + testCase(outdent` + for (let i = 0; i < cities.length; i++) { + console.log(cities[i]); + const city = foo(); + console.log(city); + } + `, outdent` + for (const city_ of cities) { + console.log(city_); + const city = foo(); + console.log(city); + } + `), + // Singularization (uses i): + testCase(outdent` + for (let i = 0; i < cities.length; i++) { + console.log(i, cities[i]); + } + `, outdent` + for (const [i, city] of cities.entries()) { + console.log(i, city); + } `) ] });