unity-find-fault
Version:
A tool to find fault in unity project.
165 lines • 7.14 kB
JavaScript
import fg from "fast-glob";
import np from "normalize-path";
import path from "path";
import { toolchain } from "../toolchain.js";
import { Project, SyntaxKind } from "ts-morph";
export class UIRelations {
static project;
static clsss = [];
static async build() {
const tsRoot = path.join(toolchain.opts.projectRoot, 'TsScripts');
const tsFiles = await fg('**/*.ts', { cwd: tsRoot, ignore: ['**/*.d.ts'] });
UIRelations.project = new Project({
tsConfigFilePath: path.join(tsRoot, 'tsconfig.json')
});
// const uiPathDataTs = path.join(tsRoot, 'System/data/UIPathData.ts');
// const tabFormTs = path.join()
for (const f of tsFiles) {
const tsf = path.join(tsRoot, f);
const src = UIRelations.project.getSourceFile(tsf);
if (!src) {
console.error('SourceFile not exists:', tsf);
continue;
}
const classes = src.getClasses();
for (const cls of classes) {
// const exts = cls.getExtends();
// if (exts) {
// console.log(f, cls.getName(), exts.getKindName(), exts.getText(), exts.getType().getText());
// }
const className = cls.getName();
if (!className)
continue;
const clsInfo = UIRelations.getClassInfo(np(tsf), className);
const btypes = cls.getBaseTypes();
if (btypes.length > 0) {
clsInfo.extends = [];
btypes.forEach(v => {
const t = v.getText();
if (!t.startsWith('import(')) {
clsInfo.extends.push(UIRelations.getClassInfo(clsInfo.sourceFile, t));
}
else {
const mch = t.match(/^import\("(.+)"\)\.(\w+)(<.+>)?$/);
if (mch) {
const [, sourceFile, className] = mch;
clsInfo.extends.push(UIRelations.getClassInfo(sourceFile + '.ts', className));
}
else {
console.error('base type parse failed: ', t);
}
}
});
}
}
}
}
static isExtends(cls, parent) {
if (cls.extends != null) {
for (const e of cls.extends) {
if (e.className == parent || UIRelations.isExtends(e, parent)) {
return true;
}
}
}
return false;
}
static getClassInfo(sourceFile, className) {
if (className.includes('<')) {
className = className.substring(0, className.indexOf('<'));
}
let clsInfo = UIRelations.clsss.find(v => v.sourceFile == sourceFile && v.className == className);
if (clsInfo == null) {
clsInfo = { sourceFile, className };
UIRelations.clsss.push(clsInfo);
}
return clsInfo;
}
static getAllForms() {
const ui2Prefab = {};
const updSrc = UIRelations.project.getSourceFileOrThrow(path.join(toolchain.opts.projectRoot, 'TsScripts/System/data/UIPathData.ts'));
const updCls = updSrc.getClassOrThrow('UIPathData');
const sps = updCls.getStaticProperties();
sps.forEach(v => {
const pd = v.asKindOrThrow(SyntaxKind.PropertyDeclaration);
ui2Prefab[pd.getName()] = pd.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral).getText();
});
const out = [];
for (const cls of UIRelations.clsss) {
if (UIRelations.isExtends(cls, 'CommonForm')) {
const info = UIRelations.getUIInfo(cls, out);
info.prefab = UIRelations.getPrefabPath(cls, ui2Prefab);
if (UIRelations.isExtends(cls, 'TabForm')) {
const subForms = UIRelations.getSubForms(cls);
// console.assert(subForms.length > 0, cls.className);
subForms.forEach(v => {
info.children.push(UIRelations.getUIInfo(v, out));
});
}
}
}
return out;
}
static getUIInfo(cls, collection) {
let info = collection.find(v => v.tsClass == cls);
if (info == null) {
info = { prefab: '', tsClass: cls, children: [] };
collection.push(info);
}
return info;
}
static getPrefabPath(cls, ui2Prefab) {
// if (cls.className == 'CommonForm' || cls.className == 'TabForm') return '';
const src = UIRelations.project.getSourceFileOrThrow(cls.sourceFile);
const clsDef = src.getClassOrThrow(cls.className);
const mthd = clsDef.getMethod('resPath');
if (mthd != null && !mthd.isAbstract()) {
const rsChilds = mthd.getBodyOrThrow().getChildrenOfKind(SyntaxKind.ReturnStatement);
if (rsChilds.length > 0) {
for (const rs of rsChilds) {
const rsText = rs.getText();
const mch = rsText.match(/return UIPathData\.(\w+)/);
if (mch != null) {
return ui2Prefab[mch[1]];
}
}
}
}
if (cls.extends != null) {
for (const e of cls.extends) {
if (UIRelations.isExtends(e, 'CommonForm')) {
return UIRelations.getPrefabPath(e, ui2Prefab);
}
}
}
return '';
}
static getSubForms(cls) {
const out = [];
const src = UIRelations.project.getSourceFileOrThrow(cls.sourceFile);
const clsDef = src.getClassOrThrow(cls.className);
const ctors = clsDef.getConstructors();
for (const ctor of ctors) {
const lines = ctor.getBody()?.getChildrenOfKind(SyntaxKind.SyntaxList);
if (lines) {
const exps = lines[0].getChildrenOfKind(SyntaxKind.ExpressionStatement);
for (const exp of exps) {
const e = exp.getExpressionIfKind(SyntaxKind.CallExpression);
if (e && e.getExpressionIfKind(SyntaxKind.SuperKeyword)) {
const args = e.getArguments();
args.filter(v => v.isKind(SyntaxKind.Identifier)).forEach(v => {
const tt = v.getType().getText();
const mch = tt.match(/^typeof import\("(.+)"\)\.(\w+)(<.+>)?$/);
if (mch) {
const [, sourceFile, className] = mch;
out.push(UIRelations.getClassInfo(sourceFile + '.ts', className));
}
});
}
}
}
}
return out;
}
}
//# sourceMappingURL=UIRelations.js.map