支持了组件复用形式的解析

This commit is contained in:
Pan Qiancheng 2024-12-08 16:03:30 +08:00
parent 57b4ecc3f5
commit 1fcf59b75e
2 changed files with 277 additions and 164 deletions

View File

@ -16,6 +16,7 @@ import {
getAttrsFromFormData,
getAttrsFromMethods,
getAttrsFromProperties,
resolveModulePath,
} from './ts-utils';
// attrs preset在isList为false的时候默认有一个oakId的属性注释先写在这里具体在下面处理怕自己忘了
@ -66,12 +67,7 @@ export const subscribeAll = (callback: (name: string) => void) => {
export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
const componentList: EntityComponentDef[] = [];
function visitNode(node: ts.Node, path: string) {
if (
ts.isCallExpression(node) &&
ts.isIdentifier(node.expression) &&
node.expression.text === 'OakComponent'
) {
function handleComponentArg(node: ts.CallExpression, path: string) {
const args = node.arguments;
if (args.length === 1 && ts.isObjectLiteralExpression(args[0])) {
const properties = args[0].properties;
@ -119,9 +115,7 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
if (fs.existsSync(configPath)) {
try {
mpConfig = JSON.parse(
fs.readFileSync(configPath, 'utf-8')
);
mpConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
} catch (e) {
console.log('读取配置文件失败:', configPath, e);
}
@ -206,9 +200,7 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length
? methodNames
: undefined,
methodNames: methodNames.length ? methodNames : undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
@ -225,9 +217,7 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length
? methodNames
: undefined,
methodNames: methodNames.length ? methodNames : undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
@ -238,15 +228,104 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
}
}
function visitNode(source: ts.SourceFile, node: ts.Node, path: string) {
// 如果是export default OakComponent()的形式
if (
ts.isExportAssignment(node) &&
ts.isCallExpression(node.expression) &&
ts.isIdentifier(node.expression.expression) &&
node.expression.expression.text === 'OakComponent'
) {
handleComponentArg(node.expression, path);
return;
}
// 如果是export default OakComponent并且OakComponent是import的
if (
ts.isExportAssignment(node) &&
node.expression &&
ts.isIdentifier(node.expression) &&
node.expression.text === 'OakComponent'
) {
// 尝试在sourceFile中找到OakComponent的import
const importStatement = source.statements.find((statement) => {
if (ts.isImportDeclaration(statement)) {
// importClause.Identifier === OakComponent
if (
statement.importClause &&
statement.importClause.name?.text === 'OakComponent'
) {
return true;
}
}
return false;
}) as ts.ImportDeclaration | undefined;
if (!importStatement) {
return;
}
const moduleSpecifier = importStatement.moduleSpecifier;
if (!ts.isStringLiteral(moduleSpecifier)) {
return;
}
const modulePath = moduleSpecifier.text;
// 如果是相对路径
if (isRelativePath(modulePath)) {
const moduleDir = join(path, '..', modulePath, 'index.ts');
const modulePathNor = normalizePath(moduleDir);
const moduleSource = ts.createSourceFile(
moduleDir,
fs.readFileSync(modulePathNor, 'utf-8'),
ts.ScriptTarget.ES2015,
true
);
ts.forEachChild(moduleSource, (node) => {
visitNode(moduleSource, node, path);
});
}
// 如果是模块导入
else {
const [sourceFile, filePath] = resolveModulePath(modulePath, path);
if (!sourceFile) {
return;
}
// 如果文件名是.d.ts结尾
if (filePath.endsWith('.d.ts')) {
// 在模块中需要去查找js文件进行解析
const jsFilePath = filePath.replace('.d.ts', '.js');
if (!fs.existsSync(jsFilePath)) {
return;
}
const sourceFile = ts.createSourceFile(
jsFilePath,
fs.readFileSync(jsFilePath, 'utf-8'),
ts.ScriptTarget.ES2015,
true
);
ts.forEachChild(sourceFile, (node) => {
visitNode(sourceFile, node, path);
});
return;
}
ts.forEachChild(sourceFile, (node) => {
visitNode(sourceFile, node, path);
});
return;
}
return;
}
ts.forEachChild(node, (node) => {
visitNode(node, path);
visitNode(source, node, path);
});
}
scanPath.forEach((dirPath) => {
const files = glob.sync(`${dirPath}/**/index.ts`);
// 保证路径是绝对路径
const absoluteFiles = files.map(filePath => path.resolve(filePath));
const absoluteFiles = files.map((filePath) => path.resolve(filePath));
absoluteFiles.forEach((filePath) => {
// 因为涉及到路径的比较,所以需要规范化路径
const normalizedPath = normalizePath(filePath);
@ -258,7 +337,7 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
);
ts.forEachChild(sourceFile, (node) => {
visitNode(node, normalizedPath);
visitNode(sourceFile, node, normalizedPath);
});
});
});

View File

@ -721,6 +721,40 @@ export const createProjectProgram = (filePath: string) => {
return program;
};
export const resolveModulePath = (
importModule: string,
file?: string
): [sourceFile: ts.SourceFile, path: string] => {
const programPath = file || join(pathConfig.oakAppDomainHome, 'index.ts');
const program = createProjectProgram(programPath);
// 创建编译器主机
const compilerHost = ts.createCompilerHost(program.getCompilerOptions());
// 解析模块
const resolvedModule = ts.resolveModuleName(
importModule,
programPath,
program.getCompilerOptions(),
compilerHost
);
if (!resolvedModule.resolvedModule) {
throw new Error(`Could not resolve module: ${importModule}`);
}
// 获取源文件
const sourceFile = program.getSourceFile(
resolvedModule.resolvedModule.resolvedFileName
);
if (!sourceFile) {
throw new Error(
`Could not find source file for module: ${importModule}`
);
}
return [sourceFile, resolvedModule.resolvedModule.resolvedFileName];
};
// 判断类型是否是 Promise
export function isPromiseType(
type: ts.Type,