Skip to content

Commit e7ad6d2

Browse files
committed
fixup! [New] Symmetric useState hook variable names
1 parent 0c97ebc commit e7ad6d2

File tree

1 file changed

+110
-83
lines changed

1 file changed

+110
-83
lines changed

lib/rules/hook-use-state.js

Lines changed: 110 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -30,101 +30,128 @@ module.exports = {
3030
schema: [],
3131
},
3232

33-
create: Components.detect((context, components, utils) => {
34-
let reactImportLocalName;
35-
let reactUseStateLocalName;
36-
37-
return {
38-
CallExpression(node) {
39-
const isReactUseStateCall = (
40-
reactImportLocalName
33+
create: Components.detect((context, components) => ({
34+
CallExpression(node) {
35+
const defaultReactImport = components.getDefaultReactImport();
36+
const defaultReactImportName = defaultReactImport ? defaultReactImport.local.name : undefined;
37+
const namedReactImports = components.getNamedReactImports();
38+
const useStateReactImportSpecifier = namedReactImports
39+
? namedReactImports.find((specifier) => specifier.imported.name === 'useState')
40+
: undefined;
41+
const useStateReactImportName = useStateReactImportSpecifier
42+
? useStateReactImportSpecifier.local.name
43+
: undefined;
44+
45+
const isReactUseStateCall = (
46+
defaultReactImportName
4147
&& node.callee.type === 'MemberExpression'
4248
&& node.callee.object.type === 'Identifier'
43-
&& node.callee.object.name === reactImportLocalName
49+
&& node.callee.object.name === defaultReactImportName
4450
&& node.callee.property.type === 'Identifier'
4551
&& node.callee.property.name === 'useState'
46-
);
52+
);
4753

48-
const isUseStateCall = (
49-
reactUseStateLocalName
54+
const isUseStateCall = (
55+
useStateReactImportName
5056
&& node.callee.type === 'Identifier'
51-
&& node.callee.name === reactUseStateLocalName
52-
);
57+
&& node.callee.name === useStateReactImportName
58+
);
59+
60+
// Ignore unless this is a useState() or React.useState() call.
61+
if (!isReactUseStateCall && !isUseStateCall) {
62+
return;
63+
}
5364

54-
// Ignore unless this is a useState() or React.useState() call.
55-
if (!isReactUseStateCall && !isUseStateCall) {
56-
return;
57-
}
65+
const isImmediateReturn = node.parent && node.parent.type === 'ReturnStatement';
66+
if (isImmediateReturn) {
67+
return;
68+
}
5869

59-
const isDestructuringDeclarator = (
60-
node.parent.type === 'VariableDeclarator'
70+
const isDestructuringDeclarator = (
71+
node.parent.type === 'VariableDeclarator'
6172
&& node.parent.id.type === 'ArrayPattern'
73+
);
74+
75+
if (!isDestructuringDeclarator) {
76+
report(
77+
context,
78+
messages.useStateErrorMessage,
79+
'useStateErrorMessage',
80+
{ node }
6281
);
82+
return;
83+
}
84+
85+
const variableNodes = node.parent.id.elements;
86+
const valueVariable = variableNodes[0];
87+
const setterVariable = variableNodes[1];
88+
89+
const valueVariableName = valueVariable
90+
? valueVariable.name
91+
: undefined;
6392

64-
if (!isDestructuringDeclarator) {
65-
report(
66-
context,
67-
messages.useStateErrorMessage,
68-
'useStateErrorMessage',
69-
{ node }
70-
);
71-
return;
72-
}
73-
74-
const variableNodes = node.parent.id.elements;
75-
const valueVariable = variableNodes[0];
76-
const setterVariable = variableNodes[1];
77-
78-
const valueVariableName = valueVariable
79-
? valueVariable.name
80-
: undefined;
81-
82-
const setterVariableName = setterVariable
83-
? setterVariable.name
84-
: undefined;
85-
86-
const expectedSetterVariableName = valueVariableName ? (
87-
`set${valueVariableName.charAt(0).toUpperCase()}${valueVariableName.slice(1)}`
88-
) : undefined;
89-
90-
if (
91-
!valueVariable
93+
const setterVariableName = setterVariable
94+
? setterVariable.name
95+
: undefined;
96+
97+
const expectedSetterVariableName = valueVariableName ? (
98+
`set${valueVariableName.charAt(0).toUpperCase()}${valueVariableName.slice(1)}`
99+
) : undefined;
100+
101+
if (
102+
!valueVariable
92103
|| !setterVariable
93104
|| setterVariableName !== expectedSetterVariableName
94105
|| variableNodes.length !== 2
95-
) {
96-
report(
97-
context,
98-
messages.useStateErrorMessage,
99-
'useStateErrorMessage',
100-
{
101-
node: node.parent.id,
102-
fix: valueVariableName ? (fixer) => fixer.replaceTextRange(
103-
[node.parent.id.range[0], node.parent.id.range[1]],
104-
`[${valueVariableName}, ${expectedSetterVariableName}]`
105-
) : undefined,
106-
}
107-
);
108-
}
109-
},
110-
ImportDeclaration(node) {
111-
const isReactImported = node.source.type === 'Literal' && node.source.value === 'react';
112-
const reactDefaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
113-
reactImportLocalName = reactDefaultSpecifier ? reactDefaultSpecifier.local.name : undefined;
114-
115-
const reactUseStateSpecifier = isReactImported
116-
? node.specifiers.find(
117-
(specifier) => (
118-
specifier.type === 'ImportSpecifier'
119-
&& specifier.imported.name === 'useState'
120-
)
121-
)
122-
: undefined;
123-
124-
reactUseStateLocalName = reactUseStateSpecifier
125-
? reactUseStateSpecifier.local.name
126-
: undefined;
127-
},
128-
};
129-
}),
106+
) {
107+
report(
108+
context,
109+
messages.useStateErrorMessage,
110+
'useStateErrorMessage',
111+
{
112+
node: node.parent.id,
113+
fix: valueVariableName ? (fixer) => fixer.replaceTextRange(
114+
[node.parent.id.range[0], node.parent.id.range[1]],
115+
`[${valueVariableName}, ${expectedSetterVariableName}]`
116+
) : undefined,
117+
}
118+
);
119+
}
120+
},
121+
})),
122+
// create: ReactImport.detect((context) => {
123+
// // context.foo = 'foo';
124+
// // const result = WithReactImport((reactLocalName, context) => {
125+
// // return {
126+
// // CallExpression(node) {
127+
128+
// // }
129+
// // }
130+
// // })
131+
// let reactImportLocalName;
132+
// let reactUseStateLocalName;
133+
134+
// return {
135+
// // Program
136+
137+
// ImportDeclaration(node) {
138+
// const isReactImported = node.source.type === 'Literal' && node.source.value === 'react';
139+
// const reactDefaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
140+
// reactImportLocalName = reactDefaultSpecifier ? reactDefaultSpecifier.local.name : undefined;
141+
142+
// const reactUseStateSpecifier = isReactImported
143+
// ? node.specifiers.find(
144+
// (specifier) => (
145+
// specifier.type === 'ImportSpecifier'
146+
// && specifier.imported.name === 'useState'
147+
// )
148+
// )
149+
// : undefined;
150+
151+
// reactUseStateLocalName = reactUseStateSpecifier
152+
// ? reactUseStateSpecifier.local.name
153+
// : undefined;
154+
// },
155+
// };
156+
// }),
130157
};

0 commit comments

Comments
 (0)