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 283ace5367..54ce1a0bc0 100644 --- a/rules/no-for-loop.js +++ b/rules/no-for-loop.js @@ -2,6 +2,7 @@ const getDocumentationUrl = require('./utils/get-documentation-url'); const isLiteralValue = require('./utils/is-literal-value'); const {flatten} = require('lodash'); +const {singular} = require('pluralize'); const defaultElementName = 'element'; const isLiteralZero = node => isLiteralValue(node, 0); @@ -261,6 +262,15 @@ const getReferencesInChildScopes = (scope, name) => { ]; }; +const getSingularName = (originalName) => { + const singularName = singular(originalName); + if (!singularName || singularName === originalName) { + // Bail if the singular name is the same as the original. + return undefined; + } + return singularName; +} + const create = context => { const sourceCode = context.getSourceCode(); const {scopeManager} = sourceCode; @@ -335,7 +345,7 @@ const create = context => { const shouldGenerateIndex = isIndexVariableUsedElsewhereInTheLoopBody(indexVariable, bodyScope, arrayIdentifierName); const index = indexIdentifierName; - const element = elementIdentifierName || defaultElementName; + const element = elementIdentifierName || getSingularName(arrayIdentifierName) || defaultElementName; const array = arrayIdentifierName; let declarationElement = element; diff --git a/test/no-for-loop.js b/test/no-for-loop.js index 24dfcdbfd3..ce2ca0da1b 100644 --- a/test/no-for-loop.js +++ b/test/no-for-loop.js @@ -482,6 +482,36 @@ ruleTester.run('no-for-loop', rule, { const [ a, b ] = element; console.log(a, b, element); } + `), + // Singularization (simple case): + testCase(outdent` + for (let i = 0; i < plugins.length; i++) { + console.log(plugins[i]); + } + `, outdent` + for (const plugin of plugins) { + console.log(plugin); + } + `), + // Singularization (more advanced case): + testCase(outdent` + for (let i = 0; i < people.length; i++) { + console.log(people[i]); + } + `, outdent` + for (const person of people) { + console.log(person); + } + `), + // Singularization (capital letters, multiple words): + testCase(outdent` + for (let i = 0; i < LARGE_CITIES.length; i++) { + console.log(LARGE_CITIES[i]); + } + `, outdent` + for (const LARGE_CITY of LARGE_CITIES) { + console.log(LARGE_CITY); + } `) ] });