Skip to content

Commit 4d2fd86

Browse files
DanielRosenwasserljharb
authored andcommitted
[Refactor] variableUtil: Avoid creating a single flat variable scope for each lookup
1 parent 6a83d67 commit 4d2fd86

13 files changed

+39
-51
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1212
* [`forbid-component-props`]: add `propNamePattern` to allow / disallow prop name patterns ([#3774][] @akulsr0)
1313
* [`jsx-handler-names`]: support ignoring component names ([#3772][] @akulsr0)
1414

15+
### Changed
16+
* [Refactor] `variableUtil`: Avoid creating a single flat variable scope for each lookup ([#3782][] @DanielRosenwasser)
17+
18+
[#3782]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3782
1519
[#3774]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3774
1620
[#3772]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3772
1721
[#3759]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3759

lib/rules/jsx-max-depth.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ module.exports = {
8888
});
8989
}
9090

91-
function findJSXElementOrFragment(variables, name, previousReferences) {
91+
function findJSXElementOrFragment(startNode, name, previousReferences) {
9292
function find(refs, prevRefs) {
9393
for (let i = refs.length - 1; i >= 0; i--) {
9494
if (has(refs[i], 'writeExpr')) {
@@ -97,14 +97,14 @@ module.exports = {
9797
return (jsxUtil.isJSX(writeExpr)
9898
&& writeExpr)
9999
|| ((writeExpr && writeExpr.type === 'Identifier')
100-
&& findJSXElementOrFragment(variables, writeExpr.name, prevRefs));
100+
&& findJSXElementOrFragment(startNode, writeExpr.name, prevRefs));
101101
}
102102
}
103103

104104
return null;
105105
}
106106

107-
const variable = variableUtil.getVariable(variables, name);
107+
const variable = variableUtil.getVariableFromContext(context, startNode, name);
108108
if (variable && variable.references) {
109109
const containDuplicates = previousReferences.some((ref) => includes(variable.references, ref));
110110

@@ -150,8 +150,7 @@ module.exports = {
150150
return;
151151
}
152152

153-
const variables = variableUtil.variablesInScope(context, node);
154-
const element = findJSXElementOrFragment(variables, node.expression.name, []);
153+
const element = findJSXElementOrFragment(node, node.expression.name, []);
155154

156155
if (element) {
157156
const baseDepth = getDepth(node);

lib/rules/jsx-no-leaked-render.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,7 @@ module.exports = {
161161
if (isCoerceValidLeftSide || getIsCoerceValidNestedLogicalExpression(leftSide)) {
162162
return;
163163
}
164-
const variables = variableUtil.variablesInScope(context, node);
165-
const leftSideVar = variableUtil.getVariable(variables, leftSide.name);
164+
const leftSideVar = variableUtil.getVariableFromContext(context, node, leftSide.name);
166165
if (leftSideVar) {
167166
const leftSideValue = leftSideVar.defs
168167
&& leftSideVar.defs.length

lib/rules/jsx-sort-default-props.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ module.exports = {
9797
*/
9898
function findVariableByName(node, name) {
9999
const variable = variableUtil
100-
.variablesInScope(context, node)
101-
.find((item) => item.name === name);
100+
.getVariableFromContext(context, node, name);
102101

103102
if (!variable || !variable.defs[0] || !variable.defs[0].node) {
104103
return null;

lib/rules/no-danger-with-children.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ module.exports = {
3232
},
3333
create(context) {
3434
function findSpreadVariable(node, name) {
35-
return variableUtil.variablesInScope(context, node)
36-
.find((item) => item.name === name);
35+
return variableUtil.getVariableFromContext(context, node, name);
3736
}
3837
/**
3938
* Takes a ObjectExpression and returns the value of the prop if it has it
@@ -128,8 +127,7 @@ module.exports = {
128127
let props = node.arguments[1];
129128

130129
if (props.type === 'Identifier') {
131-
const variable = variableUtil.variablesInScope(context, node)
132-
.find((item) => item.name === props.name);
130+
const variable = variableUtil.getVariableFromContext(context, node, props.name);
133131
if (variable && variable.defs.length && variable.defs[0].node.init) {
134132
props = variable.defs[0].node.init;
135133
}

lib/rules/react-in-jsx-scope.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ module.exports = {
3737
const pragma = pragmaUtil.getFromContext(context);
3838

3939
function checkIfReactIsInScope(node) {
40-
const variables = variableUtil.variablesInScope(context, node);
41-
if (variableUtil.findVariable(variables, pragma)) {
40+
if (variableUtil.getVariableFromContext(context, node, pragma)) {
4241
return;
4342
}
4443
report(context, messages.notInScope, 'notInScope', {

lib/rules/sort-default-props.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ module.exports = {
9191
* @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
9292
*/
9393
function findVariableByName(node, name) {
94-
const variable = variableUtil.variablesInScope(context, node)
95-
.find((item) => item.name === name);
94+
const variable = variableUtil.getVariableFromContext(context, node, name);
9695

9796
if (!variable || !variable.defs[0] || !variable.defs[0].node) {
9897
return null;

lib/rules/style-prop-object.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ module.exports = {
6161
* @param {object} node A Identifier node
6262
*/
6363
function checkIdentifiers(node) {
64-
const variable = variableUtil.variablesInScope(context, node)
65-
.find((item) => item.name === node.name);
64+
const variable = variableUtil.getVariableFromContext(context, node, node.name);
6665

6766
if (!variable || !variable.defs[0] || !variable.defs[0].node.init) {
6867
return;

lib/util/Components.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -669,14 +669,7 @@ function componentRule(rule, context) {
669669
if (!variableName) {
670670
return null;
671671
}
672-
let variableInScope;
673-
const variables = variableUtil.variablesInScope(context, node);
674-
for (i = 0, j = variables.length; i < j; i++) {
675-
if (variables[i].name === variableName) {
676-
variableInScope = variables[i];
677-
break;
678-
}
679-
}
672+
const variableInScope = variableUtil.getVariableFromContext(context, node, variableName);
680673
if (!variableInScope) {
681674
return null;
682675
}

lib/util/isDestructuredFromPragmaImport.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ const variableUtil = require('./variable');
1313
*/
1414
module.exports = function isDestructuredFromPragmaImport(context, node, variable) {
1515
const pragma = pragmaUtil.getFromContext(context);
16-
const variables = variableUtil.variablesInScope(context, node);
17-
const variableInScope = variableUtil.getVariable(variables, variable);
16+
const variableInScope = variableUtil.getVariableFromContext(context, node, variable);
1817
if (variableInScope) {
1918
const latestDef = variableUtil.getLatestVariableDefinition(variableInScope);
2019
if (latestDef) {

lib/util/propTypes.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -965,8 +965,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
965965
break;
966966
}
967967
case 'Identifier': {
968-
const firstMatchingVariable = variableUtil.variablesInScope(context, node)
969-
.find((variableInScope) => variableInScope.name === propTypes.name);
968+
const firstMatchingVariable = variableUtil.getVariableFromContext(context, node, propTypes.name);
970969
if (firstMatchingVariable) {
971970
const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1];
972971
markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init, rootNode);

lib/util/variable.js

+21-19
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
'use strict';
77

8-
const toReversed = require('array.prototype.toreversed');
98
const getScope = require('./eslint').getScope;
109

1110
/**
@@ -29,30 +28,33 @@ function getVariable(variables, name) {
2928
}
3029

3130
/**
32-
* List all variable in a given scope
33-
*
34-
* Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21
31+
* Searches for a variable in the given scope.
3532
*
3633
* @param {Object} context The current rule context.
3734
* @param {ASTNode} node The node to start looking from.
38-
* @returns {Array} The variables list
35+
* @param {string} name The name of the variable to search.
36+
* @returns {Object | undefined} Variable if the variable was found, undefined if not.
3937
*/
40-
function variablesInScope(context, node) {
38+
function getVariableFromContext(context, node, name) {
4139
let scope = getScope(context, node);
42-
let variables = scope.variables;
4340

44-
while (scope.type !== 'global') {
45-
scope = scope.upper;
46-
variables = scope.variables.concat(variables);
47-
}
48-
if (scope.childScopes.length) {
49-
variables = scope.childScopes[0].variables.concat(variables);
50-
if (scope.childScopes[0].childScopes.length) {
51-
variables = scope.childScopes[0].childScopes[0].variables.concat(variables);
41+
while (scope) {
42+
let variable = getVariable(scope.variables, name);
43+
44+
if (!variable && scope.childScopes.length) {
45+
variable = getVariable(scope.childScopes[0].variables, name);
46+
47+
if (!variable && scope.childScopes[0].childScopes.length) {
48+
variable = getVariable(scope.childScopes[0].childScopes[0].variables, name);
49+
}
5250
}
53-
}
5451

55-
return toReversed(variables);
52+
if (variable) {
53+
return variable;
54+
}
55+
scope = scope.upper;
56+
}
57+
return undefined;
5658
}
5759

5860
/**
@@ -63,7 +65,7 @@ function variablesInScope(context, node) {
6365
* @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
6466
*/
6567
function findVariableByName(context, node, name) {
66-
const variable = getVariable(variablesInScope(context, node), name);
68+
const variable = getVariableFromContext(context, node, name);
6769

6870
if (!variable || !variable.defs[0] || !variable.defs[0].node) {
6971
return null;
@@ -93,6 +95,6 @@ module.exports = {
9395
findVariable,
9496
findVariableByName,
9597
getVariable,
96-
variablesInScope,
98+
getVariableFromContext,
9799
getLatestVariableDefinition,
98100
};

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"array-includes": "^3.1.8",
2929
"array.prototype.findlast": "^1.2.5",
3030
"array.prototype.flatmap": "^1.3.2",
31-
"array.prototype.toreversed": "^1.1.2",
3231
"array.prototype.tosorted": "^1.1.4",
3332
"doctrine": "^2.1.0",
3433
"es-iterator-helpers": "^1.0.19",

0 commit comments

Comments
 (0)