Skip to content

Commit 21d886f

Browse files
author
Brian Vaughn
committed
Refactored AST utils slightly
1 parent a433505 commit 21d886f

File tree

2 files changed

+63
-76
lines changed

2 files changed

+63
-76
lines changed

packages/react-devtools-extensions/src/astUtils.js

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ const AST_NODE_TYPES = Object.freeze({
3737
});
3838

3939
// Check if line number obtained from source map and the line number in hook node match
40-
export function checkNodeLocation(path: NodePath, line: number): boolean {
40+
function checkNodeLocation(path: NodePath, line: number): boolean {
4141
const {start, end} = path.node.loc;
4242
return line >= start.line && line <= end.line;
4343
}
4444

4545
// Checks whether hookNode is a member of targetHookNode
46-
export function filterMemberNodesOfTargetHook(
46+
function filterMemberNodesOfTargetHook(
4747
targetHookNode: NodePath,
4848
hookNode: NodePath,
4949
): boolean {
@@ -56,29 +56,15 @@ export function filterMemberNodesOfTargetHook(
5656
}
5757

5858
// Checks whether hook is the first member node of a state variable declaration node
59-
export function filterMemberWithHookVariableName(hook: NodePath): boolean {
59+
function filterMemberWithHookVariableName(hook: NodePath): boolean {
6060
return (
6161
hook.node.init.property.type === AST_NODE_TYPES.NUMERIC_LITERAL &&
6262
hook.node.init.property.value === 0
6363
);
6464
}
6565

66-
// Map the generated source line and column position to the original source and line.
67-
export function mapCompiledLineNumberToOriginalLineNumber(
68-
consumer: SourceConsumer,
69-
lineNumber: number,
70-
columnNumber: number,
71-
): number | null {
72-
const {line} = consumer.originalPositionFor({
73-
line: lineNumber,
74-
column: columnNumber,
75-
});
76-
77-
return line;
78-
}
79-
8066
// Returns all AST Nodes associated with 'potentialReactHookASTNode'
81-
export function getFilteredHookASTNodes(
67+
function getFilteredHookASTNodes(
8268
potentialReactHookASTNode: NodePath,
8369
potentialHooksFound: Array<NodePath>,
8470
source: string,
@@ -117,6 +103,47 @@ export function getFilteredHookASTNodes(
117103

118104
// Returns Hook name
119105
export function getHookName(
106+
hook: HooksNode,
107+
originalSourceAST: mixed,
108+
originalSourceCode: string,
109+
originalSourceLineNumber: number,
110+
): string | null {
111+
const hooksFromAST = getPotentialHookDeclarationsFromAST(originalSourceAST);
112+
113+
const potentialReactHookASTNode = hooksFromAST.find(node => {
114+
const nodeLocationCheck = checkNodeLocation(
115+
node,
116+
((originalSourceLineNumber: any): number),
117+
);
118+
const hookDeclaractionCheck = isConfirmedHookDeclaration(node);
119+
return nodeLocationCheck && hookDeclaractionCheck;
120+
});
121+
122+
if (!potentialReactHookASTNode) {
123+
return null;
124+
}
125+
126+
// nodesAssociatedWithReactHookASTNode could directly be used to obtain the hook variable name
127+
// depending on the type of potentialReactHookASTNode
128+
try {
129+
const nodesAssociatedWithReactHookASTNode = getFilteredHookASTNodes(
130+
potentialReactHookASTNode,
131+
hooksFromAST,
132+
originalSourceCode,
133+
);
134+
135+
return getHookNameFromNode(
136+
hook,
137+
nodesAssociatedWithReactHookASTNode,
138+
potentialReactHookASTNode,
139+
);
140+
} catch (error) {
141+
console.error(error);
142+
return null;
143+
}
144+
}
145+
146+
function getHookNameFromNode(
120147
originalHook: HooksNode,
121148
nodesAssociatedWithReactHookASTNode: NodePath[],
122149
potentialReactHookASTNode: NodePath,
@@ -193,7 +220,7 @@ export function getHookName(
193220
}
194221

195222
// Extracts the variable name from hook node path
196-
export function getHookVariableName(
223+
function getHookVariableName(
197224
hook: NodePath,
198225
isCustomHook: boolean = false,
199226
): string | null {
@@ -210,9 +237,7 @@ export function getHookVariableName(
210237
}
211238
}
212239

213-
export function getPotentialHookDeclarationsFromAST(
214-
sourceAST: File,
215-
): NodePath[] {
240+
function getPotentialHookDeclarationsFromAST(sourceAST: File): NodePath[] {
216241
const potentialHooksFound: NodePath[] = [];
217242
traverse(sourceAST, {
218243
enter(path) {
@@ -225,7 +250,7 @@ export function getPotentialHookDeclarationsFromAST(
225250
}
226251

227252
// Check if 'path' contains declaration of the form const X = useState(0);
228-
export function isConfirmedHookDeclaration(path: NodePath): boolean {
253+
function isConfirmedHookDeclaration(path: NodePath): boolean {
229254
const node = path.node.init;
230255
if (node.type !== AST_NODE_TYPES.CALL_EXPRESSION) {
231256
return false;
@@ -235,7 +260,7 @@ export function isConfirmedHookDeclaration(path: NodePath): boolean {
235260
}
236261

237262
// We consider hooks to be a hook name identifier or a member expression containing a hook name.
238-
export function isHook(node: Node): boolean {
263+
function isHook(node: Node): boolean {
239264
if (node.type === AST_NODE_TYPES.IDENTIFIER) {
240265
return isHookName(node.name);
241266
} else if (
@@ -259,7 +284,7 @@ export function isHook(node: Node): boolean {
259284
// Catch all identifiers that begin with "use"
260285
// followed by an uppercase Latin character to exclude identifiers like "user".
261286
// Copied from packages/eslint-plugin-react-hooks/src/RulesOfHooks
262-
export function isHookName(name: string): boolean {
287+
function isHookName(name: string): boolean {
263288
return /^use[A-Z0-9].*$/.test(name);
264289
}
265290

@@ -271,7 +296,7 @@ export function isNonDeclarativePrimitiveHook(hook: HooksNode) {
271296
}
272297

273298
// Check if the AST Node COULD be a React Hook
274-
export function isPotentialHookDeclaration(path: NodePath): boolean {
299+
function isPotentialHookDeclaration(path: NodePath): boolean {
275300
// The array potentialHooksFound will contain all potential hook declaration cases we support
276301
const nodePathInit = path.node.init;
277302
if (nodePathInit != null) {
@@ -305,7 +330,7 @@ export function isPotentialHookDeclaration(path: NodePath): boolean {
305330
}
306331

307332
/// Check whether 'node' is hook decalration of form useState(0); OR React.useState(0);
308-
export function isReactFunction(node: Node, functionName: string): boolean {
333+
function isReactFunction(node: Node, functionName: string): boolean {
309334
return (
310335
node.name === functionName ||
311336
(node.type === 'MemberExpression' &&
@@ -315,15 +340,15 @@ export function isReactFunction(node: Node, functionName: string): boolean {
315340
}
316341

317342
// Check if 'path' is either State or Reducer hook
318-
export function isStateOrReducerHook(path: NodePath): boolean {
343+
function isStateOrReducerHook(path: NodePath): boolean {
319344
const callee = path.node.init.callee;
320345
return (
321346
isReactFunction(callee, 'useState') || isReactFunction(callee, 'useReducer')
322347
);
323348
}
324349

325350
// Check whether hookNode of a declaration contains obvious variable name
326-
export function nodeContainsHookVariableName(hookNode: NodePath): boolean {
351+
function nodeContainsHookVariableName(hookNode: NodePath): boolean {
327352
// We determine cases where variable names are obvious in declarations. Examples:
328353
// const [tick, setTick] = useState(1); OR const ref = useRef(null);
329354
// Here tick/ref are obvious hook variables in the hook declaration node itself

packages/react-devtools-extensions/src/parseHookNames.js

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,7 @@
1212
import {parse} from '@babel/parser';
1313
import {enableHookNameParsing} from 'react-devtools-feature-flags';
1414
import {SourceMapConsumer} from 'source-map';
15-
import {
16-
checkNodeLocation,
17-
getFilteredHookASTNodes,
18-
getHookName,
19-
getPotentialHookDeclarationsFromAST,
20-
isConfirmedHookDeclaration,
21-
isNonDeclarativePrimitiveHook,
22-
mapCompiledLineNumberToOriginalLineNumber,
23-
} from './astUtils';
15+
import {getHookName, isNonDeclarativePrimitiveHook} from './astUtils';
2416
import {sourceMapsAreAppliedToErrors} from './ErrorTester';
2517

2618
import type {
@@ -245,52 +237,22 @@ function findHookNames(
245237
// Either way, we don't need to convert the Error stack frame locations.
246238
originalSourceLineNumber = lineNumber;
247239
} else {
248-
originalSourceLineNumber = mapCompiledLineNumberToOriginalLineNumber(
249-
sourceConsumer,
250-
lineNumber,
251-
columnNumber,
252-
);
240+
originalSourceLineNumber = sourceConsumer.originalPositionFor({
241+
line: lineNumber,
242+
column: columnNumber,
243+
}).line;
253244
}
254245

255246
if (originalSourceLineNumber === null) {
256247
return null;
257248
}
258249

259-
const hooksFromAST = getPotentialHookDeclarationsFromAST(
250+
return getHookName(
251+
hook,
260252
hookSourceData.originalSourceAST,
253+
((hookSourceData.originalSourceCode: any): string),
254+
((originalSourceLineNumber: any): number),
261255
);
262-
const potentialReactHookASTNode = hooksFromAST.find(node => {
263-
const nodeLocationCheck = checkNodeLocation(
264-
node,
265-
((originalSourceLineNumber: any): number),
266-
);
267-
const hookDeclaractionCheck = isConfirmedHookDeclaration(node);
268-
return nodeLocationCheck && hookDeclaractionCheck;
269-
});
270-
271-
if (!potentialReactHookASTNode) {
272-
return null;
273-
}
274-
275-
// nodesAssociatedWithReactHookASTNode could directly be used to obtain the hook variable name
276-
// depending on the type of potentialReactHookASTNode
277-
try {
278-
const originalSourceCode = ((hookSourceData.originalSourceCode: any): string);
279-
const nodesAssociatedWithReactHookASTNode = getFilteredHookASTNodes(
280-
potentialReactHookASTNode,
281-
hooksFromAST,
282-
originalSourceCode,
283-
);
284-
285-
return getHookName(
286-
hook,
287-
nodesAssociatedWithReactHookASTNode,
288-
potentialReactHookASTNode,
289-
);
290-
} catch (error) {
291-
console.error(error);
292-
return null;
293-
}
294256
}),
295257
): any): Promise<HookNames>);
296258
}

0 commit comments

Comments
 (0)