refactor(javascript): share Babel child traversal

This commit is contained in:
morluto 2026-10-10 04:58:27 +08:00
parent 617cd84d27
commit 0f10499245
3 changed files with 13 additions and 46 deletions

View File

@ -26,7 +26,10 @@ import {
import { evaluateSemanticExpression } from "./javascriptSemanticValues.js";
import { range } from "./javascriptStaticAnalysisHelpers.js";
import { semanticReturnCoverage } from "./javascriptSemanticCoverage.js";
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import {
childNodes,
traverseJavaScriptAst,
} from "./javascriptSemanticTraversal.js";
interface ReturnExpression {
readonly node: t.Node | null;
@ -145,15 +148,6 @@ const directReturnExpressions = (callable: t.Node): ReturnExpression[] => {
return output;
};
const childNodes = (node: t.Node): t.Node[] =>
(t.VISITOR_KEYS[node.type] ?? []).flatMap((key) => {
const value: unknown = Reflect.get(node, key);
if (t.isNode(value)) return [value];
return Array.isArray(value)
? value.filter((item): item is t.Node => t.isNode(item))
: [];
});
const callableIdsForBinding = (
binding: JavaScriptSemanticBindingState,
state: JavaScriptSemanticAnalysisState,

View File

@ -50,7 +50,8 @@ interface TraversalFrame {
nextIndex: number;
}
const childNodes = (node: t.Node): t.Node[] => {
/** Return child Babel nodes in visitor-key order. */
export const childNodes = (node: t.Node): t.Node[] => {
const keys = t.VISITOR_KEYS[node.type];
if (keys === undefined) return [];
const children: t.Node[] = [];

View File

@ -6,6 +6,7 @@ import type {
Node,
StringLiteral,
} from "@babel/types";
import { traverseJavaScriptAst } from "./javascript/javascriptSemanticTraversal.js";
import {
isCallExpression,
isExportAllDeclaration,
@ -15,7 +16,6 @@ import {
isImportDeclaration,
isImportExpression,
isMemberExpression,
isNode,
isStringLiteral,
isTSExternalModuleReference,
isTSImportEqualsDeclaration,
@ -201,39 +201,6 @@ const isRequireCallee = (callee: Node | null | undefined): boolean => {
return false;
};
const collectModuleExpressions = (
node: unknown,
targets: Array<CallExpression | ImportExpression>,
): void => {
if (node === null || node === undefined) return;
if (typeof node !== "object") return;
if (Array.isArray(node)) {
for (const item of node) collectModuleExpressions(item, targets);
return;
}
if (!isNode(node)) return;
if (isCallExpression(node) || isImportExpression(node)) {
targets.push(node);
}
for (const value of Object.values(node)) {
if (
value !== null &&
value !== undefined &&
typeof value === "object" &&
!isSourceLocation(value)
) {
collectModuleExpressions(value, targets);
}
}
};
const isSourceLocation = (value: unknown): boolean =>
typeof value === "object" &&
value !== null &&
"start" in value &&
"end" in value &&
!("type" in value);
const extractRequireAndDynamicImports = (
body: readonly Node[],
from_path: string,
@ -241,7 +208,12 @@ const extractRequireAndDynamicImports = (
): void => {
const expressions: Array<CallExpression | ImportExpression> = [];
for (const statement of body)
collectModuleExpressions(statement, expressions);
traverseJavaScriptAst(statement, {
enter: (node) => {
if (isCallExpression(node) || isImportExpression(node))
expressions.push(node);
},
});
for (const call of expressions) {
if (isImportExpression(call)) {