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

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, getAttrsFromFormData,
getAttrsFromMethods, getAttrsFromMethods,
getAttrsFromProperties, getAttrsFromProperties,
resolveModulePath,
} from './ts-utils'; } from './ts-utils';
// attrs preset在isList为false的时候默认有一个oakId的属性注释先写在这里具体在下面处理怕自己忘了 // attrs preset在isList为false的时候默认有一个oakId的属性注释先写在这里具体在下面处理怕自己忘了
@ -66,187 +67,265 @@ export const subscribeAll = (callback: (name: string) => void) => {
export const scanComponents = (scanPath: string[]): EntityComponentDef[] => { export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
const componentList: EntityComponentDef[] = []; const componentList: EntityComponentDef[] = [];
function visitNode(node: ts.Node, path: string) { function handleComponentArg(node: ts.CallExpression, path: string) {
const args = node.arguments;
if (args.length === 1 && ts.isObjectLiteralExpression(args[0])) {
const properties = args[0].properties;
const entity = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'entity'
);
const isList = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'isList'
);
const formData = properties.find((prop) => {
return (
(ts.isPropertyAssignment(prop) ||
ts.isMethodDeclaration(prop)) &&
ts.isIdentifier(prop.name) &&
prop.name.text === 'formData'
);
}) as ts.MethodDeclaration | ts.PropertyAssignment | undefined;
const method = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'methods'
);
const property = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'properties'
);
const datas = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'data'
);
let mpConfig: MPConfig | undefined;
const configPath = join(path, '../index.json');
if (fs.existsSync(configPath)) {
try {
mpConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
} catch (e) {
console.log('读取配置文件失败:', configPath, e);
}
}
let formDataAttrs: DocumentValue[] = [];
let methodNames: DocumentValue[] = [];
let propertiesAttrs: DocumentValue[] = [];
let datasAttrs: DocumentValue[] = [];
// 获取formData下的block 下的 returnStatement 下的ObjectLiteralExpression 下的properties
if (formData) {
formDataAttrs = getAttrsFromFormData(formData);
}
if (method) {
methodNames = getAttrsFromMethods(method);
}
if (property) {
propertiesAttrs = getAttrsFromProperties(property);
}
if (datas) {
datasAttrs = getAttrsFromDatas(datas);
}
if (entity && isList) {
if (
ts.isShorthandPropertyAssignment(entity) ||
ts.isShorthandPropertyAssignment(isList)
) {
console.log('ShorthandPropertyAssignment 还不支持');
return;
}
if (
ts.isSpreadAssignment(entity) ||
ts.isSpreadAssignment(isList)
) {
console.log('SpreadAssignment 还不支持');
return;
}
// MethodDeclaration
if (
ts.isMethodDeclaration(entity) ||
ts.isMethodDeclaration(isList)
) {
console.log('MethodDeclaration 还不支持');
return;
}
//GetAccessorDeclaration
if (
ts.isGetAccessorDeclaration(entity) ||
ts.isGetAccessorDeclaration(isList)
) {
console.log('GetAccessorDeclaration 还不支持');
return;
}
// SetAccessorDeclaration
if (
ts.isSetAccessorDeclaration(entity) ||
ts.isSetAccessorDeclaration(isList)
) {
console.log('SetAccessorDeclaration 还不支持');
return;
}
const listed = isList.initializer.getText() === 'true';
if (!listed) {
// 如果不是列表那么默认有一个oakId属性
formDataAttrs.push({
value: 'oakId',
pos: {
start: 0,
end: 0,
},
});
}
// 这里的path是整个文件夹的路径
componentList.push({
path: join(path, '..'),
entityName: entity.initializer.getText().slice(1, -1),
isList: listed,
components: [],
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length ? methodNames : undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
datas: datasAttrs.length ? datasAttrs : undefined,
mpConfig,
});
} else {
// 是一个Virtual虚拟节点没有entity和isList
componentList.push({
path: join(path, '..'),
entityName: '',
isList: false,
components: [],
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length ? methodNames : undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
datas: datasAttrs.length ? datasAttrs : undefined,
mpConfig,
});
}
}
}
function visitNode(source: ts.SourceFile, node: ts.Node, path: string) {
// 如果是export default OakComponent()的形式
if ( if (
ts.isCallExpression(node) && 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) && ts.isIdentifier(node.expression) &&
node.expression.text === 'OakComponent' node.expression.text === 'OakComponent'
) { ) {
const args = node.arguments; // 尝试在sourceFile中找到OakComponent的import
if (args.length === 1 && ts.isObjectLiteralExpression(args[0])) { const importStatement = source.statements.find((statement) => {
const properties = args[0].properties; if (ts.isImportDeclaration(statement)) {
const entity = properties.find( // importClause.Identifier === OakComponent
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'entity'
);
const isList = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'isList'
);
const formData = properties.find((prop) => {
return (
(ts.isPropertyAssignment(prop) ||
ts.isMethodDeclaration(prop)) &&
ts.isIdentifier(prop.name) &&
prop.name.text === 'formData'
);
}) as ts.MethodDeclaration | ts.PropertyAssignment | undefined;
const method = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'methods'
);
const property = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'properties'
);
const datas = properties.find(
(prop) =>
ts.isPropertyAssignment(prop) &&
prop.name.getText() === 'data'
);
let mpConfig: MPConfig | undefined;
const configPath = join(path, '../index.json');
if (fs.existsSync(configPath)) {
try {
mpConfig = JSON.parse(
fs.readFileSync(configPath, 'utf-8')
);
} catch (e) {
console.log('读取配置文件失败:', configPath, e);
}
}
let formDataAttrs: DocumentValue[] = [];
let methodNames: DocumentValue[] = [];
let propertiesAttrs: DocumentValue[] = [];
let datasAttrs: DocumentValue[] = [];
// 获取formData下的block 下的 returnStatement 下的ObjectLiteralExpression 下的properties
if (formData) {
formDataAttrs = getAttrsFromFormData(formData);
}
if (method) {
methodNames = getAttrsFromMethods(method);
}
if (property) {
propertiesAttrs = getAttrsFromProperties(property);
}
if (datas) {
datasAttrs = getAttrsFromDatas(datas);
}
if (entity && isList) {
if ( if (
ts.isShorthandPropertyAssignment(entity) || statement.importClause &&
ts.isShorthandPropertyAssignment(isList) statement.importClause.name?.text === 'OakComponent'
) { ) {
console.log('ShorthandPropertyAssignment 还不支持'); return true;
return;
} }
if (
ts.isSpreadAssignment(entity) ||
ts.isSpreadAssignment(isList)
) {
console.log('SpreadAssignment 还不支持');
return;
}
// MethodDeclaration
if (
ts.isMethodDeclaration(entity) ||
ts.isMethodDeclaration(isList)
) {
console.log('MethodDeclaration 还不支持');
return;
}
//GetAccessorDeclaration
if (
ts.isGetAccessorDeclaration(entity) ||
ts.isGetAccessorDeclaration(isList)
) {
console.log('GetAccessorDeclaration 还不支持');
return;
}
// SetAccessorDeclaration
if (
ts.isSetAccessorDeclaration(entity) ||
ts.isSetAccessorDeclaration(isList)
) {
console.log('SetAccessorDeclaration 还不支持');
return;
}
const listed = isList.initializer.getText() === 'true';
if (!listed) {
// 如果不是列表那么默认有一个oakId属性
formDataAttrs.push({
value: 'oakId',
pos: {
start: 0,
end: 0,
},
});
}
// 这里的path是整个文件夹的路径
componentList.push({
path: join(path, '..'),
entityName: entity.initializer.getText().slice(1, -1),
isList: listed,
components: [],
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length
? methodNames
: undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
datas: datasAttrs.length ? datasAttrs : undefined,
mpConfig,
});
} else {
// 是一个Virtual虚拟节点没有entity和isList
componentList.push({
path: join(path, '..'),
entityName: '',
isList: false,
components: [],
formDataAttrs: formDataAttrs.length
? formDataAttrs
: undefined,
methodNames: methodNames.length
? methodNames
: undefined,
propertiesAttrs: propertiesAttrs.length
? propertiesAttrs
: undefined,
datas: datasAttrs.length ? datasAttrs : undefined,
mpConfig,
});
} }
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) => { ts.forEachChild(node, (node) => {
visitNode(node, path); visitNode(source, node, path);
}); });
} }
scanPath.forEach((dirPath) => { scanPath.forEach((dirPath) => {
const files = glob.sync(`${dirPath}/**/index.ts`); 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) => { absoluteFiles.forEach((filePath) => {
// 因为涉及到路径的比较,所以需要规范化路径 // 因为涉及到路径的比较,所以需要规范化路径
const normalizedPath = normalizePath(filePath); const normalizedPath = normalizePath(filePath);
@ -258,7 +337,7 @@ export const scanComponents = (scanPath: string[]): EntityComponentDef[] => {
); );
ts.forEachChild(sourceFile, (node) => { ts.forEachChild(sourceFile, (node) => {
visitNode(node, normalizedPath); visitNode(sourceFile, node, normalizedPath);
}); });
}); });
}); });

View File

@ -721,6 +721,40 @@ export const createProjectProgram = (filePath: string) => {
return program; 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 // 判断类型是否是 Promise
export function isPromiseType( export function isPromiseType(
type: ts.Type, type: ts.Type,