Skip to content

Commit 874732b

Browse files
committed
[Refactor] remove array-includes, string.prototype.includes
node 12+ has a working version, and given that this is an eslint plugin, the robustness it provides is not needed
1 parent d20bd65 commit 874732b

17 files changed

+35
-54
lines changed

__mocks__/genInteractives.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44

55
import { dom, roles } from 'aria-query';
6-
import includes from 'array-includes';
76
import fromEntries from 'object.fromentries';
87

98
import JSXAttributeMock from './JSXAttributeMock';
@@ -141,17 +140,17 @@ const interactiveRoles = []
141140
)
142141
.filter((role) => (
143142
!roles.get(role).abstract
144-
&& roles.get(role).superClass.some((klasses) => includes(klasses, 'widget'))
143+
&& roles.get(role).superClass.some((klasses) => klasses.includes('widget'))
145144
));
146145

147146
const nonInteractiveRoles = roleNames
148147
.filter((role) => (
149148
!roles.get(role).abstract
150-
&& !roles.get(role).superClass.some((klasses) => includes(klasses, 'widget'))
149+
&& !roles.get(role).superClass.some((klasses) => klasses.includes('widget'))
151150

152151
// 'toolbar' does not descend from widget, but it does support
153152
// aria-activedescendant, thus in practice we treat it as a widget.
154-
&& !includes(['toolbar'], role)
153+
&& role !== 'toolbar'
155154
));
156155

157156
export function genElementSymbol(openingElement: Object): string {

__tests__/src/rules/interactive-supports-focus-test.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
// Requirements
88
// -----------------------------------------------------------------------------
99

10-
import includes from 'array-includes';
1110
import { RuleTester } from 'eslint';
1211
import {
1312
eventHandlers,
@@ -213,12 +212,12 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
213212
...alwaysValid,
214213
...passReducer(
215214
interactiveRoles,
216-
eventHandlers.filter((handler) => !includes(triggeringHandlers, handler)),
215+
eventHandlers.filter((handler) => !triggeringHandlers.includes(handler)),
217216
codeTemplate,
218217
),
219218
...passReducer(
220-
interactiveRoles.filter((role) => !includes(recommendedRoles, role)),
221-
eventHandlers.filter((handler) => includes(triggeringHandlers, handler)),
219+
interactiveRoles.filter((role) => !recommendedRoles.includes(role)),
220+
eventHandlers.filter((handler) => triggeringHandlers.includes(handler)),
222221
tabindexTemplate,
223222
),
224223
))
@@ -228,7 +227,7 @@ ruleTester.run(`${ruleName}:recommended`, rule, {
228227
...neverValid,
229228
...failReducer(recommendedRoles, triggeringHandlers, tabbableTemplate),
230229
...failReducer(
231-
interactiveRoles.filter((role) => !includes(recommendedRoles, role)),
230+
interactiveRoles.filter((role) => !recommendedRoles.includes(role)),
232231
triggeringHandlers,
233232
focusableTemplate,
234233
),
@@ -242,12 +241,12 @@ ruleTester.run(`${ruleName}:strict`, rule, {
242241
...alwaysValid,
243242
...passReducer(
244243
interactiveRoles,
245-
eventHandlers.filter((handler) => !includes(triggeringHandlers, handler)),
244+
eventHandlers.filter((handler) => !triggeringHandlers.includes(handler)),
246245
codeTemplate,
247246
),
248247
...passReducer(
249-
interactiveRoles.filter((role) => !includes(strictRoles, role)),
250-
eventHandlers.filter((handler) => includes(triggeringHandlers, handler)),
248+
interactiveRoles.filter((role) => !strictRoles.includes(role)),
249+
eventHandlers.filter((handler) => triggeringHandlers.includes(handler)),
251250
tabindexTemplate,
252251
),
253252
))
@@ -257,7 +256,7 @@ ruleTester.run(`${ruleName}:strict`, rule, {
257256
...neverValid,
258257
...failReducer(strictRoles, triggeringHandlers, tabbableTemplate),
259258
...failReducer(
260-
interactiveRoles.filter((role) => !includes(strictRoles, role)),
259+
interactiveRoles.filter((role) => !strictRoles.includes(role)),
261260
triggeringHandlers,
262261
focusableTemplate,
263262
),

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
"license": "MIT",
7777
"dependencies": {
7878
"aria-query": "^5.3.2",
79-
"array-includes": "^3.1.8",
8079
"array.prototype.flatmap": "^1.3.2",
8180
"ast-types-flow": "^0.0.8",
8281
"axe-core": "^4.10.0",
@@ -89,8 +88,7 @@
8988
"language-tags": "^1.0.9",
9089
"minimatch": "^3.1.2",
9190
"object.fromentries": "^2.0.8",
92-
"safe-regex-test": "^1.0.3",
93-
"string.prototype.includes": "^2.0.1"
91+
"safe-regex-test": "^1.0.3"
9492
},
9593
"peerDependencies": {
9694
"eslint": "^8.8 || ^9.13"

src/rules/control-has-associated-label.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import { getProp, getLiteralPropValue } from 'jsx-ast-utils';
1313
import type { JSXElement } from 'ast-types-flow';
14-
import includes from 'array-includes';
1514
import { generateObjSchema, arraySchema } from '../util/schemas';
1615
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
1716
import getElementType from '../util/getElementType';
@@ -68,7 +67,7 @@ export default ({
6867
return;
6968
}
7069
// Ignore roles that are "interactive" but should not require a label.
71-
if (includes(ignoreRoles, role)) {
70+
if (ignoreRoles.includes(role)) {
7271
return;
7372
}
7473
const props = node.openingElement.attributes;

src/rules/img-redundant-alt.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
// ----------------------------------------------------------------------------
99

1010
import { getProp, getLiteralPropValue } from 'jsx-ast-utils';
11-
import includes from 'array-includes';
12-
import stringIncludes from 'string.prototype.includes';
1311
import safeRegexTest from 'safe-regex-test';
1412
import { generateObjSchema, arraySchema } from '../util/schemas';
1513
import getElementType from '../util/getElementType';
@@ -34,9 +32,9 @@ function containsRedundantWord(value, redundantWords) {
3432
const lowercaseRedundantWords = redundantWords.map((redundantWord) => redundantWord.toLowerCase());
3533

3634
if (isASCII(value)) {
37-
return value.split(/\s+/).some((valueWord) => includes(lowercaseRedundantWords, valueWord.toLowerCase()));
35+
return value.split(/\s+/).some((valueWord) => lowercaseRedundantWords.includes(valueWord.toLowerCase()));
3836
}
39-
return lowercaseRedundantWords.some((redundantWord) => stringIncludes(value.toLowerCase(), redundantWord));
37+
return lowercaseRedundantWords.some((redundantWord) => value.toLowerCase().includes(redundantWord));
4038
}
4139

4240
export default {

src/rules/interactive-supports-focus.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
hasAnyProp,
1616
} from 'jsx-ast-utils';
1717
import type { JSXOpeningElement } from 'ast-types-flow';
18-
import includes from 'array-includes';
1918
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
2019
import {
2120
enumArraySchema,
@@ -39,7 +38,7 @@ const schema = generateObjSchema({
3938
// TODO: convert to use iterFilter and iterFrom
4039
tabbable: enumArraySchema([...roles.keys()].filter((name) => (
4140
!roles.get(name).abstract
42-
&& roles.get(name).superClass.some((klasses) => includes(klasses, 'widget'))
41+
&& roles.get(name).superClass.some((klasses) => klasses.includes('widget'))
4342
))),
4443
});
4544

@@ -95,7 +94,7 @@ export default ({
9594
&& !hasTabindex
9695
) {
9796
const role = getLiteralPropValue(getProp(attributes, 'role'));
98-
if (includes(tabbable, role)) {
97+
if (tabbable.includes(role)) {
9998
// Always tabbable, tabIndex = 0
10099
context.report({
101100
node,

src/rules/no-interactive-element-to-noninteractive-role.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
propName,
1717
} from 'jsx-ast-utils';
1818
import type { JSXIdentifier } from 'ast-types-flow';
19-
import includes from 'array-includes';
2019
import hasOwn from 'hasown';
2120
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
2221
import type { ESLintJSXAttribute } from '../../flow/eslint-jsx';
@@ -68,7 +67,7 @@ export default ({
6867
// Allow overrides from rule configuration for specific elements and
6968
// roles.
7069
const allowedRoles = (options[0] || {});
71-
if (hasOwn(allowedRoles, type) && includes(allowedRoles[type], role)) {
70+
if (hasOwn(allowedRoles, type) && allowedRoles[type].includes(role)) {
7271
return;
7372
}
7473
if (

src/rules/no-noninteractive-element-interactions.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
propName,
1717
} from 'jsx-ast-utils';
1818
import type { JSXOpeningElement } from 'ast-types-flow';
19-
import includes from 'array-includes';
2019
import hasOwn from 'hasown';
2120
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
2221
import { arraySchema, generateObjSchema } from '../util/schemas';
@@ -62,7 +61,7 @@ export default ({
6261
const interactiveProps = config.handlers || defaultInteractiveProps;
6362
// Allow overrides from rule configuration for specific elements and roles.
6463
if (hasOwn(config, type)) {
65-
attributes = attributes.filter((attr) => attr.type !== 'JSXSpreadAttribute' && !includes(config[type], propName(attr)));
64+
attributes = attributes.filter((attr) => attr.type !== 'JSXSpreadAttribute' && !config[type].includes(propName(attr)));
6665
}
6766

6867
const hasInteractiveProps = interactiveProps

src/rules/no-noninteractive-element-to-interactive-role.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
propName,
1515
} from 'jsx-ast-utils';
1616
import type { JSXIdentifier } from 'ast-types-flow';
17-
import includes from 'array-includes';
1817
import hasOwn from 'hasown';
1918
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
2019
import type { ESLintJSXAttribute } from '../../flow/eslint-jsx';
@@ -66,7 +65,7 @@ export default ({
6665
// Allow overrides from rule configuration for specific elements and
6766
// roles.
6867
const allowedRoles = (options[0] || {});
69-
if (hasOwn(allowedRoles, type) && includes(allowedRoles[type], role)) {
68+
if (hasOwn(allowedRoles, type) && allowedRoles[type].includes(role)) {
7069
return;
7170
}
7271
if (

src/rules/no-noninteractive-tabindex.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
getProp,
1515
getLiteralPropValue,
1616
} from 'jsx-ast-utils';
17-
import includes from 'array-includes';
1817
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
1918
import getElementType from '../util/getElementType';
2019
import isInteractiveElement from '../util/isInteractiveElement';
@@ -71,10 +70,10 @@ export default ({
7170
roles,
7271
allowExpressionValues,
7372
} = (options[0] || {});
74-
if (tags && includes(tags, type)) {
73+
if (tags && tags.includes(type)) {
7574
return;
7675
}
77-
if (roles && includes(roles, role)) {
76+
if (roles && roles.includes(role)) {
7877
return;
7978
}
8079
if (

src/rules/no-redundant-roles.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Rule Definition
1010
// ----------------------------------------------------------------------------
1111

12-
import includes from 'array-includes';
1312
import hasOwn from 'hasown';
1413
import type { JSXOpeningElement } from 'ast-types-flow';
1514
import type { ESLintConfig, ESLintContext, ESLintVisitorSelectorConfig } from '../../flow/eslint';
@@ -64,7 +63,7 @@ export default ({
6463
redundantRolesForElement = DEFAULT_ROLE_EXCEPTIONS[type] || [];
6564
}
6665

67-
if (includes(redundantRolesForElement, implicitRole)) {
66+
if (redundantRolesForElement.includes(implicitRole)) {
6867
return;
6968
}
7069

src/util/getElementType.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import type { JSXOpeningElement } from 'ast-types-flow';
66
import hasOwn from 'hasown';
7-
import includes from 'array-includes';
87
import { elementType, getProp, getLiteralPropValue } from 'jsx-ast-utils';
98

109
import type { ESLintContext } from '../../flow/eslint';
@@ -22,7 +21,7 @@ const getElementType = (context: ESLintContext): ((node: JSXOpeningElement) => s
2221
let rawType = elementType(node);
2322
if (
2423
polymorphicProp
25-
&& (!polymorphicAllowList || includes(polymorphicAllowList, rawType))
24+
&& (!polymorphicAllowList || polymorphicAllowList.includes(rawType))
2625
) {
2726
rawType = polymorphicProp;
2827
}

src/util/isInteractiveElement.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
AXObjects,
1212
elementAXObjects,
1313
} from 'axobject-query';
14-
import includes from 'array-includes';
1514
import flatMap from 'array.prototype.flatmap';
1615
import iterFrom from 'es-iterator-helpers/Iterator.from';
1716
// import iterFlatMap from 'es-iterator-helpers/Iterator.prototype.flatMap';
@@ -31,7 +30,7 @@ const nonInteractiveRoles = new Set(roleKeys
3130
// 'toolbar' does not descend from widget, but it does support
3231
// aria-activedescendant, thus in practice we treat it as a widget.
3332
&& name !== 'toolbar'
34-
&& !role.superClass.some((classes) => includes(classes, 'widget'))
33+
&& !role.superClass.some((classes) => classes.includes('widget'))
3534
);
3635
}).concat(
3736
// The `progressbar` is descended from `widget`, but in practice, its
@@ -47,7 +46,7 @@ const interactiveRoles = new Set(roleKeys
4746
// The `progressbar` is descended from `widget`, but in practice, its
4847
// value is always `readonly`, so we treat it as a non-interactive role.
4948
&& name !== 'progressbar'
50-
&& role.superClass.some((classes) => includes(classes, 'widget'))
49+
&& role.superClass.some((classes) => classes.includes('widget'))
5150
);
5251
}).concat(
5352
// 'toolbar' does not descend from widget, but it does support

src/util/isInteractiveRole.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
import { roles as rolesMap } from 'aria-query';
33
import type { Node } from 'ast-types-flow';
44
import { getProp, getLiteralPropValue } from 'jsx-ast-utils';
5-
import includes from 'array-includes';
65
import flatMap from 'array.prototype.flatmap';
76

87
const roles = [...rolesMap.keys()];
98
const interactiveRoles = roles.filter((name) => (
109
!rolesMap.get(name).abstract
11-
&& rolesMap.get(name).superClass.some((klasses) => includes(klasses, 'widget'))
10+
&& rolesMap.get(name).superClass.some((klasses) => klasses.includes('widget'))
1211
));
1312

1413
// 'toolbar' does not descend from widget, but it does support
@@ -42,11 +41,11 @@ const isInteractiveRole = (
4241
const normalizedValues = String(value).toLowerCase().split(' ');
4342
const validRoles = flatMap(
4443
normalizedValues,
45-
(name: string) => (includes(roles, name) ? [name] : []),
44+
(name: string) => (roles.includes(name) ? [name] : []),
4645
);
4746
if (validRoles.length > 0) {
4847
// The first role value is a series takes precedence.
49-
isInteractive = includes(interactiveRoles, validRoles[0]);
48+
isInteractive = interactiveRoles.includes(validRoles[0]);
5049
}
5150

5251
return isInteractive;

src/util/isNonInteractiveElement.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
elementAXObjects,
1313
} from 'axobject-query';
1414
import type { Node } from 'ast-types-flow';
15-
import includes from 'array-includes';
1615
import flatMap from 'array.prototype.flatmap';
1716
import iterFrom from 'es-iterator-helpers/Iterator.from';
1817
// import iterFlatMap from 'es-iterator-helpers/Iterator.prototype.flatMap';
@@ -35,7 +34,7 @@ const nonInteractiveRoles = new Set(roleKeys
3534
// This role is meant to have no semantic value.
3635
// @see https://www.w3.org/TR/wai-aria-1.2/#generic
3736
&& name !== 'generic'
38-
&& !role.superClass.some((classes) => includes(classes, 'widget'))
37+
&& !role.superClass.some((classes) => classes.includes('widget'))
3938
);
4039
}).concat(
4140
// The `progressbar` is descended from `widget`, but in practice, its
@@ -54,7 +53,7 @@ const interactiveRoles = new Set(roleKeys
5453
// This role is meant to have no semantic value.
5554
// @see https://www.w3.org/TR/wai-aria-1.2/#generic
5655
&& name !== 'generic'
57-
&& role.superClass.some((classes) => includes(classes, 'widget'))
56+
&& role.superClass.some((classes) => classes.includes('widget'))
5857
);
5958
}).concat(
6059
// 'toolbar' does not descend from widget, but it does support
@@ -74,7 +73,7 @@ const nonInteractiveElementRoleSchemas = flatMap(
7473
([elementSchema, rolesArr]) => (rolesArr.every((role): boolean => nonInteractiveRoles.has(role)) ? [elementSchema] : []),
7574
);
7675

77-
const nonInteractiveAXObjects = new Set(filter(iterFrom(AXObjects.keys()), (name) => includes(['window', 'structure'], AXObjects.get(name).type)));
76+
const nonInteractiveAXObjects = new Set(filter(iterFrom(AXObjects.keys()), (name) => ['window', 'structure'].includes(AXObjects.get(name).type)));
7877

7978
// TODO: convert to use iterFlatMap and iterFrom
8079
const nonInteractiveElementAXObjectSchemas = flatMap(

src/util/isNonInteractiveRole.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import {
88
} from 'aria-query';
99
import type { Node } from 'ast-types-flow';
1010
import { getProp, getLiteralPropValue } from 'jsx-ast-utils';
11-
import includes from 'array-includes';
1211
import flatMap from 'array.prototype.flatmap';
1312

1413
const nonInteractiveRoles = [...rolesMap.keys()].filter((name) => (
1514
!rolesMap.get(name).abstract
16-
&& !rolesMap.get(name).superClass.some((klasses) => includes(klasses, 'widget'))
15+
&& !rolesMap.get(name).superClass.some((klasses) => klasses.includes('widget'))
1716
));
1817

1918
/**
@@ -54,7 +53,7 @@ const isNonInteractiveRole = (
5453
);
5554
if (validRoles.length > 0) {
5655
// The first role value is a series takes precedence.
57-
isNonInteractive = includes(nonInteractiveRoles, validRoles[0]);
56+
isNonInteractive = nonInteractiveRoles.includes(validRoles[0]);
5857
}
5958

6059
return isNonInteractive;

0 commit comments

Comments
 (0)