Skip to content

Commit e31bcf0

Browse files
refactor(oxlint-plugin): consolidate two copies of isAstDescendant
The 7-line helper that walks an AST parent chain to test ancestor membership was duplicated byte-identical in: - rules/react-builtins/exhaustive-deps-symbol-stability.ts - semantic/closure-captures.ts Promote to utils/is-ast-descendant.ts. Both callsites now import. Behaviour-neutral. Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
1 parent 53e2ccf commit e31bcf0

3 files changed

Lines changed: 21 additions & 18 deletions

File tree

packages/oxlint-plugin-react-doctor/src/plugin/rules/react-builtins/exhaustive-deps-symbol-stability.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ScopeAnalysis, SymbolDescriptor } from "../../semantic/scope-analy
33
import type { EsTreeNode } from "../../utils/es-tree-node.js";
44
import type { EsTreeNodeOfType } from "../../utils/es-tree-node-of-type.js";
55
import { getStaticTemplateLiteralValue } from "../../utils/get-static-template-literal-value.js";
6+
import { isAstDescendant } from "../../utils/is-ast-descendant.js";
67
import { isNodeOfType } from "../../utils/is-node-of-type.js";
78
import {
89
getHookName,
@@ -122,15 +123,6 @@ export const getFunctionValueNode = (symbol: SymbolDescriptor): EsTreeNode | nul
122123
return null;
123124
};
124125

125-
const isAstDescendant = (inner: EsTreeNode, outer: EsTreeNode): boolean => {
126-
let current: EsTreeNode | null | undefined = inner;
127-
while (current) {
128-
if (current === outer) return true;
129-
current = current.parent ?? null;
130-
}
131-
return false;
132-
};
133-
134126
export const isRecursiveInitializerCapture = (
135127
symbol: SymbolDescriptor,
136128
callback: EsTreeNode,

packages/oxlint-plugin-react-doctor/src/plugin/semantic/closure-captures.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { EsTreeNode } from "../utils/es-tree-node.js";
22
import type { ReferenceDescriptor, ScopeAnalysis } from "./scope-analysis.js";
33
import { isDescendantScope } from "./scope-analysis.js";
4+
import { isAstDescendant } from "../utils/is-ast-descendant.js";
45
import { isAstNode } from "../utils/is-ast-node.js";
56
import { isFunctionLike } from "../utils/is-function-like.js";
67

@@ -15,15 +16,6 @@ const TYPE_ONLY_CHILD_KEYS: ReadonlySet<string> = new Set([
1516

1617
// True if `inner` is a descendant of `outer` (or equal) in the AST
1718
// tree. Used to filter references inside `functionNode`.
18-
const isAstDescendant = (inner: EsTreeNode, outer: EsTreeNode): boolean => {
19-
let current: EsTreeNode | null | undefined = inner;
20-
while (current) {
21-
if (current === outer) return true;
22-
current = current.parent ?? null;
23-
}
24-
return false;
25-
};
26-
2719
// Returns every reference inside `functionNode`'s body whose binding
2820
// lives OUTSIDE the function — i.e. the closure-captured set. Useful
2921
// for exhaustive-deps to compute the actual set of values a hook
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { EsTreeNode } from "./es-tree-node.js";
2+
3+
/**
4+
* True when `inner` is `outer` itself or any descendant in the AST
5+
* `parent` chain. Walks `inner.parent` upward and stops at either a
6+
* match (`true`) or the chain's root (`false`).
7+
*
8+
* Was duplicated byte-identical in:
9+
* - exhaustive-deps-symbol-stability.ts
10+
* - semantic/closure-captures.ts
11+
*/
12+
export const isAstDescendant = (inner: EsTreeNode, outer: EsTreeNode): boolean => {
13+
let current: EsTreeNode | null | undefined = inner;
14+
while (current) {
15+
if (current === outer) return true;
16+
current = current.parent ?? null;
17+
}
18+
return false;
19+
};

0 commit comments

Comments
 (0)