diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 0d00c051e2..f1c7420a7f 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const propsUtil = require('../util/props'); const docsUrl = require('../util/docsUrl'); @@ -362,10 +364,8 @@ module.exports = { return; } - const list = components.list(); - - Object.keys(list).forEach((component) => { - const annotation = getComponentTypeAnnotation(list[component]); + values(components.list()).forEach((component) => { + const annotation = getComponentTypeAnnotation(component); if (annotation) { let propType; @@ -380,15 +380,15 @@ module.exports = { if (propType) { [].concat(propType).forEach((prop) => { validatePropNaming( - list[component].node, + component.node, prop.properties || prop.members ); }); } } - if (list[component].invalidProps && list[component].invalidProps.length > 0) { - reportInvalidNaming(list[component]); + if (component.invalidProps && component.invalidProps.length > 0) { + reportInvalidNaming(component); } }); diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index 85e1632d62..ba3db9328d 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -6,6 +6,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -91,15 +93,15 @@ module.exports = { return { 'Program:exit'() { - const list = components.list(); - // If no defaultProps could be found, we don't report anything. - Object.keys(list).filter((component) => list[component].defaultProps).forEach((component) => { - reportInvalidDefaultProps( - list[component].declaredPropTypes, - list[component].defaultProps || {} - ); - }); + values(components.list()) + .filter((component) => component.defaultProps) + .forEach((component) => { + reportInvalidDefaultProps( + component.declaredPropTypes, + component.defaultProps || {} + ); + }); }, }; }), diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index f5c72dcc94..2ea6653351 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -120,11 +120,7 @@ module.exports = { function checkDescendant(baseDepth, children) { baseDepth += 1; - (children || []).forEach((node) => { - if (!hasJSX(node)) { - return; - } - + (children || []).filter((node) => hasJSX(node)).forEach((node) => { if (baseDepth > maxDepth) { report(node, baseDepth); } else if (!isLeaf(node)) { diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index 74929cc60e..5de5bee1d6 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -7,6 +7,8 @@ const propName = require('jsx-ast-utils/propName'); const includes = require('array-includes'); +const toSorted = require('array.prototype.tosorted'); + const docsUrl = require('../util/docsUrl'); const jsxUtil = require('../util/jsx'); const report = require('../util/report'); @@ -239,7 +241,7 @@ function generateFixerFunction(node, context, reservedList) { const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context); const sortedAttributeGroups = sortableAttributeGroups .slice(0) - .map((group) => group.slice(0).sort((a, b) => contextCompare(a, b, options))); + .map((group) => toSorted(group, (a, b) => contextCompare(a, b, options))); return function fixFunction(fixer) { const fixers = []; diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js index ac7beb1804..8c99314d0e 100644 --- a/lib/rules/no-deprecated.js +++ b/lib/rules/no-deprecated.js @@ -189,10 +189,7 @@ module.exports = { if (!isReactImport) { return; } - node.specifiers.forEach((specifier) => { - if (!specifier.imported) { - return; - } + node.specifiers.filter(((s) => s.imported)).forEach((specifier) => { checkDeprecation(node, `${MODULES[node.source.value][0]}.${specifier.imported.name}`); }); }, @@ -212,10 +209,8 @@ module.exports = { ) { return; } - node.id.properties.forEach((property) => { - if (property.type !== 'RestElement' && property.key) { - checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`); - } + node.id.properties.filter((p) => p.type !== 'RestElement' && p.key).forEach((property) => { + checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`); }); }, diff --git a/lib/rules/no-direct-mutation-state.js b/lib/rules/no-direct-mutation-state.js index 8b6839f31e..08d77e9bca 100644 --- a/lib/rules/no-direct-mutation-state.js +++ b/lib/rules/no-direct-mutation-state.js @@ -6,6 +6,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); @@ -141,13 +143,11 @@ module.exports = { }, 'Program:exit'() { - const list = components.list(); - - Object.keys(list).forEach((key) => { - if (!isValid(list[key])) { - reportMutations(list[key]); - } - }); + values(components.list()) + .filter((component) => !isValid(component)) + .forEach((component) => { + reportMutations(component); + }); }, }; }), diff --git a/lib/rules/no-multi-comp.js b/lib/rules/no-multi-comp.js index 1e5aa66c8b..31b9ef0d4e 100644 --- a/lib/rules/no-multi-comp.js +++ b/lib/rules/no-multi-comp.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -64,15 +66,14 @@ module.exports = { return; } - const list = components.list(); - - Object.keys(list).filter((component) => !isIgnored(list[component])).forEach((component, i) => { - if (i >= 1) { + values(components.list()) + .filter((component) => !isIgnored(component)) + .slice(1) + .forEach((component) => { report(context, messages.onlyOneComponent, 'onlyOneComponent', { - node: list[component].node, + node: component.node, }); - } - }); + }); }, }; }), diff --git a/lib/rules/no-object-type-as-default-prop.js b/lib/rules/no-object-type-as-default-prop.js index ef8d63a368..2ba25889d8 100644 --- a/lib/rules/no-object-type-as-default-prop.js +++ b/lib/rules/no-object-type-as-default-prop.js @@ -37,14 +37,10 @@ function hasUsedObjectDestructuringSyntax(params) { function verifyDefaultPropsDestructuring(context, properties) { // Loop through each of the default params - properties.filter((prop) => prop.type === 'Property').forEach((prop) => { + properties.filter((prop) => prop.type === 'Property' && prop.value.type === 'AssignmentPattern').forEach((prop) => { const propName = prop.key.name; const propDefaultValue = prop.value; - if (propDefaultValue.type !== 'AssignmentPattern') { - return; - } - const propDefaultValueType = propDefaultValue.right.type; if ( @@ -95,14 +91,13 @@ module.exports = { create: Components.detect((context, components) => ({ 'Program:exit'() { const list = components.list(); - values(list).forEach((component) => { - const node = component.node; - if (!hasUsedObjectDestructuringSyntax(node.params)) { - return; - } - const properties = node.params[0].properties; - verifyDefaultPropsDestructuring(context, properties); - }); + values(list) + .filter((component) => hasUsedObjectDestructuringSyntax(component.node.params)) + .forEach((component) => { + const node = component.node; + const properties = node.params[0].properties; + verifyDefaultPropsDestructuring(context, properties); + }); }, })), }; diff --git a/lib/rules/no-set-state.js b/lib/rules/no-set-state.js index b91d10814f..486eb211ff 100644 --- a/lib/rules/no-set-state.js +++ b/lib/rules/no-set-state.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -75,10 +77,11 @@ module.exports = { }, 'Program:exit'() { - const list = components.list(); - Object.keys(list).filter((component) => !isValid(list[component])).forEach((component) => { - reportSetStateUsages(list[component]); - }); + values(components.list()) + .filter((component) => !isValid(component)) + .forEach((component) => { + reportSetStateUsages(component); + }); }, }; }), diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index f1f727f029..ba826d523d 100644 --- a/lib/rules/no-typos.js +++ b/lib/rules/no-typos.js @@ -247,11 +247,9 @@ module.exports = { return; } - node.properties.forEach((property) => { - if (property.type !== 'SpreadElement') { - reportErrorIfPropertyCasingTypo(property.value, property.key, false); - reportErrorIfLifecycleMethodCasingTypo(property); - } + node.properties.filter((property) => property.type !== 'SpreadElement').forEach((property) => { + reportErrorIfPropertyCasingTypo(property.value, property.key, false); + reportErrorIfLifecycleMethodCasingTypo(property); }); }, }; diff --git a/lib/rules/no-unused-class-component-methods.js b/lib/rules/no-unused-class-component-methods.js index 3531177d2d..96664d6c52 100644 --- a/lib/rules/no-unused-class-component-methods.js +++ b/lib/rules/no-unused-class-component-methods.js @@ -245,11 +245,11 @@ module.exports = { // detect `{ foo, bar: baz } = this` if (node.init && isThisExpression(node.init) && node.id.type === 'ObjectPattern') { - node.id.properties.forEach((prop) => { - if (prop.type === 'Property' && isKeyLiteralLike(prop, prop.key)) { + node.id.properties + .filter((prop) => prop.type === 'Property' && isKeyLiteralLike(prop, prop.key)) + .forEach((prop) => { addUsedProperty(prop.key); - } - }); + }); } }, }; diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index 9259462a45..a3cf352aac 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + // As for exceptions for props.children or props.className (and alike) look at // https://github.com/jsx-eslint/eslint-plugin-react/issues/7 @@ -159,14 +161,12 @@ module.exports = { return { 'Program:exit'() { - const list = components.list(); // Report undeclared proptypes for all classes - Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => { - if (!mustBeValidated(list[component])) { - return; - } - reportUnusedPropTypes(list[component]); - }); + values(components.list()) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + reportUnusedPropTypes(component); + }); }, }; }), diff --git a/lib/rules/prefer-read-only-props.js b/lib/rules/prefer-read-only-props.js index a851051989..a0353b38c0 100644 --- a/lib/rules/prefer-read-only-props.js +++ b/lib/rules/prefer-read-only-props.js @@ -5,6 +5,9 @@ 'use strict'; +const flatMap = require('array.prototype.flatmap'); +const values = require('object.values'); + const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -49,17 +52,12 @@ module.exports = { create: Components.detect((context, components) => ({ 'Program:exit'() { - const list = components.list(); - - Object.keys(list).forEach((key) => { - const component = list[key]; - - if (!component.declaredPropTypes) { - return; - } - - Object.keys(component.declaredPropTypes).forEach((propName) => { - const prop = component.declaredPropTypes[propName]; + flatMap( + values(components.list()), + (component) => component.declaredPropTypes || [] + ).forEach((declaredPropTypes) => { + Object.keys(declaredPropTypes).forEach((propName) => { + const prop = declaredPropTypes[propName]; if (!prop.node || !isFlowPropertyType(prop.node)) { return; diff --git a/lib/rules/prefer-stateless-function.js b/lib/rules/prefer-stateless-function.js index 0fc027c879..2743ea38aa 100644 --- a/lib/rules/prefer-stateless-function.js +++ b/lib/rules/prefer-stateless-function.js @@ -7,6 +7,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const testReactVersion = require('../util/version').testReactVersion; const astUtil = require('../util/ast'); @@ -363,29 +365,25 @@ module.exports = { 'Program:exit'() { const list = components.list(); - Object.keys(list).forEach((component) => { - if ( - hasOtherProperties(list[component].node) - || list[component].useThis - || list[component].useRef - || list[component].invalidReturn - || list[component].hasChildContextTypes - || list[component].useDecorators - || ( - !componentUtil.isES5Component(list[component].node, context) - && !componentUtil.isES6Component(list[component].node, context) + values(list) + .filter((component) => ( + !hasOtherProperties(component.node) + && !component.useThis + && !component.useRef + && !component.invalidReturn + && !component.hasChildContextTypes + && !component.useDecorators + && !component.hasSCU + && ( + componentUtil.isES5Component(component.node, context) + || componentUtil.isES6Component(component.node, context) ) - ) { - return; - } - - if (list[component].hasSCU) { - return; - } - report(context, messages.componentShouldBePure, 'componentShouldBePure', { - node: list[component].node, + )) + .forEach((component) => { + report(context, messages.componentShouldBePure, 'componentShouldBePure', { + node: component.node, + }); }); - }); }, }; }), diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 2be4feced9..bb317a2467 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -8,6 +8,8 @@ // As for exceptions for props.children or props.className (and alike) look at // https://github.com/jsx-eslint/eslint-plugin-react/issues/7 +const values = require('object.values'); + const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -188,9 +190,11 @@ module.exports = { 'Program:exit'() { const list = components.list(); // Report undeclared proptypes for all classes - Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => { - reportUndeclaredPropTypes(list[component]); - }); + values(list) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + reportUndeclaredPropTypes(component); + }); }, }; }), diff --git a/lib/rules/require-optimization.js b/lib/rules/require-optimization.js index 3a91c51d76..05bd071fc6 100644 --- a/lib/rules/require-optimization.js +++ b/lib/rules/require-optimization.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const componentUtil = require('../util/componentUtil'); const docsUrl = require('../util/docsUrl'); @@ -225,12 +227,12 @@ module.exports = { }, 'Program:exit'() { - const list = components.list(); - // Report missing shouldComponentUpdate for all components - Object.keys(list).filter((component) => !list[component].hasSCU).forEach((component) => { - reportMissingOptimization(list[component]); - }); + values(components.list()) + .filter((component) => !component.hasSCU) + .forEach((component) => { + reportMissingOptimization(component); + }); }, }; }), diff --git a/lib/rules/require-render-return.js b/lib/rules/require-render-return.js index 4148e0a930..cb06206952 100644 --- a/lib/rules/require-render-return.js +++ b/lib/rules/require-render-return.js @@ -5,6 +5,8 @@ 'use strict'; +const values = require('object.values'); + const Components = require('../util/Components'); const astUtil = require('../util/ast'); const componentUtil = require('../util/componentUtil'); @@ -82,22 +84,20 @@ module.exports = { }, 'Program:exit'() { - const list = components.list(); - Object.keys(list).forEach((component) => { - if ( - !findRenderMethod(list[component].node) - || list[component].hasReturnStatement - || ( - !componentUtil.isES5Component(list[component].node, context) - && !componentUtil.isES6Component(list[component].node, context) + values(components.list()) + .filter((component) => ( + findRenderMethod(component.node) + && !component.hasReturnStatement + && ( + componentUtil.isES5Component(component.node, context) + || componentUtil.isES6Component(component.node, context) ) - ) { - return; - } - report(context, messages.noRenderReturn, 'noRenderReturn', { - node: findRenderMethod(list[component].node), + )) + .forEach((component) => { + report(context, messages.noRenderReturn, 'noRenderReturn', { + node: findRenderMethod(component.node), + }); }); - }); }, }; }), diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js index 120fceee22..b56187a298 100644 --- a/lib/rules/sort-comp.js +++ b/lib/rules/sort-comp.js @@ -7,6 +7,7 @@ const has = require('object.hasown/polyfill')(); const entries = require('object.entries'); +const values = require('object.values'); const arrayIncludes = require('array-includes'); const Components = require('../util/Components'); @@ -431,9 +432,8 @@ module.exports = { return { 'Program:exit'() { - const list = components.list(); - Object.keys(list).forEach((component) => { - const properties = astUtil.getComponentProperties(list[component].node); + values(components.list()).forEach((component) => { + const properties = astUtil.getComponentProperties(component.node); checkPropsOrder(properties); }); diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index d0428e0630..afe1e7fa99 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -260,7 +260,6 @@ module.exports = { } }); }, - }; }, }; diff --git a/lib/util/Components.js b/lib/util/Components.js index 53acf1b4d6..c661883095 100644 --- a/lib/util/Components.js +++ b/lib/util/Components.js @@ -181,7 +181,7 @@ class Components { */ length() { const list = Lists.get(this); - return Object.keys(list).filter((i) => list[i].confidence >= 2).length; + return values(list).filter((component) => component.confidence >= 2).length; } /** diff --git a/lib/util/propTypesSort.js b/lib/util/propTypesSort.js index cbb5f97e3a..0bf8998128 100644 --- a/lib/util/propTypesSort.js +++ b/lib/util/propTypesSort.js @@ -4,6 +4,8 @@ 'use strict'; +const toSorted = require('array.prototype.tosorted'); + const astUtil = require('./ast'); /** @@ -159,9 +161,10 @@ function fixPropTypesSort(fixer, context, declarations, ignoreCase, requiredFirs }, [[]]); nodeGroups.forEach((nodes) => { - const sortedAttributes = nodes - .slice() - .sort((a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast)); + const sortedAttributes = toSorted( + nodes, + (a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast) + ); source = nodes.reduceRight((acc, attr, index) => { const sortedAttr = sortedAttributes[index]; diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js index f20c22bbe9..3597bb4010 100644 --- a/lib/util/usedPropTypes.js +++ b/lib/util/usedPropTypes.js @@ -4,6 +4,8 @@ 'use strict'; +const values = require('object.values'); + const astUtil = require('./ast'); const componentUtil = require('./componentUtil'); const testReactVersion = require('./version').testReactVersion; @@ -557,11 +559,11 @@ module.exports = function usedPropTypesInstructions(context, components, utils) }, 'Program:exit'() { - const list = components.list(); - - Object.keys(list).filter((component) => mustBeValidated(list[component])).forEach((component) => { - handleCustomValidators(list[component]); - }); + values(components.list()) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + handleCustomValidators(component); + }); }, }; }; diff --git a/package.json b/package.json index 7c71c1217e..b5037653b1 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "array-includes": "^3.1.5", "array.prototype.flatmap": "^1.3.0", + "array.prototype.tosorted": "^1.1.0", "doctrine": "^2.1.0", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", diff --git a/tests/index.js b/tests/index.js index 41c050a56a..7068473986 100644 --- a/tests/index.js +++ b/tests/index.js @@ -6,6 +6,8 @@ const assert = require('assert'); const fs = require('fs'); const path = require('path'); const arrayIncludes = require('array-includes'); +const flatMap = require('array.prototype.flatmap'); +const toSorted = require('array.prototype.tosorted'); const plugin = require('..'); @@ -33,16 +35,20 @@ describe('rule documentation files have the correct content', () => { }; function getConfigsForRule(ruleName, checkForEnabled) { - const configNames = []; - Object.keys(plugin.configs).forEach((configName) => { - const value = plugin.configs[configName].rules[`react/${ruleName}`]; - const isOn = arrayIncludes([2, 'error'], value); - const isOff = arrayIncludes([0, 'off'], value); - if (value !== undefined && ((checkForEnabled && isOn) || (!checkForEnabled && isOff))) { - configNames.push(configName); - } - }); - return configNames.sort(); + return toSorted( + flatMap( + Object.keys(plugin.configs), + (configName) => { + const value = plugin.configs[configName].rules[`react/${ruleName}`]; + const isOn = arrayIncludes([2, 'error'], value); + const isOff = arrayIncludes([0, 'off'], value); + if (value !== undefined && ((checkForEnabled && isOn) || (!checkForEnabled && isOff))) { + return configName; + } + return []; + } + ) + ); } function configNamesToList(configNames) {