feat(config-editor): add AST parser (speed/enumRef/raw) with tests

This commit is contained in:
panFD
2026-06-21 00:06:28 +08:00
parent 7bb5f8bacc
commit 0a960b737c
2 changed files with 112 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
import * as ts from 'typescript';
import { RecordValue } from './recordValue';
/** 在 SourceFile 中查找 `export const <name> = {...}`,返回其对象字面量节点。 */
export function findExportObjectLiteral(src: ts.SourceFile, name: string): ts.ObjectLiteralExpression | null {
let result: ts.ObjectLiteralExpression | null = null;
const visit = (node: ts.Node) => {
if (result) return;
if (ts.isVariableStatement(node)) {
for (const d of node.declarationList.declarations) {
if (d.name.getText() === name && d.initializer && ts.isObjectLiteralExpression(d.initializer)) {
result = d.initializer;
return;
}
}
}
ts.forEachChild(node, visit);
};
visit(src);
return result;
}
/** 识别 `AtkSpeedSet[AtkSpeedLv.<level>].cd`,否则 null。 */
function tryParseSpeedExpr(node: ts.PropertyAccessExpression): RecordValue | null {
if (node.name.text !== 'cd') return null;
const inner = node.expression;
if (!ts.isElementAccessExpression(inner)) return null;
if (!ts.isIdentifier(inner.expression) || inner.expression.text !== 'AtkSpeedSet') return null;
const arg = inner.argumentExpression;
if (!ts.isPropertyAccessExpression(arg)) return null;
if (!ts.isIdentifier(arg.expression) || arg.expression.text !== 'AtkSpeedLv') return null;
return { kind: 'speed', level: arg.name.text };
}
export function parseExpression(node: ts.Expression): RecordValue {
if (ts.isNumericLiteral(node)) return { kind: 'num', value: Number(node.text) };
if (ts.isStringLiteral(node)) return { kind: 'str', value: node.text };
if (node.kind === ts.SyntaxKind.TrueKeyword) return { kind: 'bool', value: true };
if (node.kind === ts.SyntaxKind.FalseKeyword) return { kind: 'bool', value: false };
if (ts.isPropertyAccessExpression(node)) {
const speed = tryParseSpeedExpr(node);
if (speed) return speed;
if (ts.isIdentifier(node.expression)) {
return { kind: 'enumRef', qualifier: node.expression.text, member: node.name.text };
}
return { kind: 'raw', text: node.getText() }; // 多级 a.b.c 等
}
if (ts.isArrayLiteralExpression(node)) {
return { kind: 'arr', items: node.elements.map(e => parseExpression(e)) };
}
if (ts.isObjectLiteralExpression(node)) {
const props: Record<string, RecordValue> = {};
for (const m of node.properties) {
if (ts.isPropertyAssignment(m)) {
props[m.name.getText()] = parseExpression(m.initializer);
}
}
return { kind: 'obj', props };
}
return { kind: 'raw', text: node.getText() }; // 二元/模板/调用/其他元素访问
}