1/* 2* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15 16import * as vscode from 'vscode'; 17import * as path from 'path'; 18import * as ts from 'typescript'; 19import { json } from 'stream/consumers'; 20import internal = require('stream'); 21import { ParamObj, FuncObj, ClassObj, StructObj, EnumObj, TypeObj, ParseObj } from '../gen/datatype' 22import { Logger } from '../common/log'; 23 24const fs = require('fs'); 25 26interface TypeArguments { 27 pos: number; 28 members: []; 29} 30 31interface NameObj { 32 pos: number; 33 escapedText: string; 34} 35 36interface TypeObject { 37 pos: number; 38 escapedText: string; 39} 40 41interface MemberObj { 42 pos: number; 43 name: NameObj; 44 type: TypeObject; 45} 46 47 48const NUMBER_TYPE = 148; 49const STRING_TYPE = 152; 50const BOOLEAN_TYPE = 134; 51const VOID_TYPE = 114; 52const ARRAY_TYPE = 185; 53const OBJECT_TYPE = 180; 54 55let gchecker: ts.TypeChecker; 56 57export function getTypeAliasSubtypes(typeAlias: ts.TypeAliasDeclaration, list: ParamObj[]) { 58 // 检查类型是否为类型节点 59 const typeNode = typeAlias.type; 60 // Logger.getInstance().debug('getTypeAliasSubtypes'); 61 try { 62 if (ts.isUnionTypeNode(typeNode)) { 63 // 如果是联合类型(Union Type),遍历它的子类型 64 Logger.getInstance().debug('isUnionTypeNode'); 65 return typeNode.types.map(type => JSON.stringify(type.getText())); 66 } else if (ts.isIntersectionTypeNode(typeNode)) { 67 // 如果是交叉类型(Intersection Type),遍历它的子类型 68 Logger.getInstance().debug('isIntersectionTypeNode'); 69 return typeNode.types.map(type => JSON.stringify(type.decorators)); 70 } else if (ts.isTypeLiteralNode(typeNode)) { 71 // 如果是类型字面量(Type Literal),遍历它的属性 72 Logger.getInstance().debug('isTypeLiteralNode'); 73 return typeNode.members.map(member => { 74 let nameStr = JSON.stringify(member.name); 75 let nameObj = JSON.parse(nameStr); 76 let propType = gchecker.getTypeAtLocation(member); 77 let typeStr = gchecker.typeToString(propType); 78 let kindStr = typeStr; 79 list.push({ 80 type: kindStr, 81 name: nameObj.escapedText, 82 arraySize: 0, 83 arraySizeList: [] 84 }) 85 return `(${nameObj.escapedText}:${kindStr})`; 86 }); 87 } 88 // 处理其他类型 89 return [JSON.stringify(typeNode)]; 90 } catch (error) { 91 Logger.getInstance().error('Error processing node:' + error); 92 } 93 return []; 94} 95 96export function getParamType(paramType: any) { 97 if (paramType === undefined) { 98 return 'void'; 99 } 100 Logger.getInstance().info('getParamType: ' + paramType.kind) 101 // 类型为 number 102 let paramText = paramType.kind === NUMBER_TYPE ? 'number' : 103 // 类型为 string 104 paramType.kind === STRING_TYPE ? 'string' : 105 // 类型为 boolean 106 paramType.kind === BOOLEAN_TYPE ? 'boolean' : 107 paramType.kind === VOID_TYPE ? 'void' : 108 // 默认any类型 109 'any'; 110 if (paramType.kind === OBJECT_TYPE) { 111 const type = paramType.typeName.escapedText; 112 if (paramType.typeArguments) { 113 const subType = paramType.typeArguments[0].kind === NUMBER_TYPE ? 'number' : 114 paramType.typeArguments[0].kind === STRING_TYPE ? 'string' : 115 paramType.typeArguments[0].kind === BOOLEAN_TYPE ? 'boolean' : 'any'; 116 if (type === 'Array') { 117 paramText = 'Array<' + subType + '>'; 118 } 119 } else { 120 return type 121 } 122 } 123 if (paramType.kind === ARRAY_TYPE) { 124 const subType = paramType.elementType.kind === NUMBER_TYPE ? 'number' : 125 paramType.elementType.kind === STRING_TYPE ? 'string' : 126 paramType.elementType.kind === BOOLEAN_TYPE ? 'boolean' : 127 'any'; 128 paramText = 'Array<' + subType + '>'; 129 } 130 return paramText; 131} 132 133export function doParseTs(filePath: string, sourceCode: string): ParseObj { 134 let parseRes: ParseObj = { 135 enums: [], 136 unions: [], 137 structs: [], 138 classes: [], 139 funcs: [], 140 types: [], 141 } 142 function visitor(node: ts.Node) { 143 if (ts.isClassDeclaration(node) && node.name) { 144 Logger.getInstance().debug(`Class: ${node.name.text}, ${node.members}`); 145 let classItem: ClassObj = { 146 name: node.name.text, 147 alias: '', 148 functionList: [], 149 variableList: [] 150 }; 151 try { 152 node.members.forEach(member => { 153 // Logger.getInstance().debug(`Member: ${JSON.stringify(member)}`) 154 if (ts.isMethodDeclaration(member) && member.name) { 155 const methodstr = member.name ? JSON.stringify(member.name) : 'noname'; 156 const methodObject = JSON.parse(methodstr); 157 const methodName = methodObject.escapedText; 158 Logger.getInstance().debug(`memberName: ${methodName} `); 159 let returnStr = 'void'; 160 if (member.type) { 161 let returnObjStr = JSON.stringify(member.type); 162 // Logger.getInstance().debug(`returnObjStr: ${returnObjStr} `); 163 let returnObj = JSON.parse(returnObjStr); 164 returnStr = member.type?.getText(sourceFile); //getParamType(member.type); 165 if (returnObj.typeName) { 166 let returnNameStr = JSON.stringify(returnObj.typeName); 167 let returnName = JSON.parse(returnNameStr).escapedText; 168 let returnArgsStr = JSON.stringify(returnObj.typeArguments); 169 let returnArgsObj = JSON.parse(returnArgsStr); 170 const returnArgs = returnArgsObj.map((argItem: TypeArguments) => { 171 if (argItem.members) { 172 173 } 174 let argStr = argItem.members ? argItem.members.map((memItem: MemberObj) => { 175 let memNameStr = ''; 176 let memTypeStr = 'void'; 177 if (memItem.name) { 178 memNameStr = memItem.name.escapedText; 179 } 180 if (memItem.type) { 181 memTypeStr = memItem.type.escapedText ? memItem.type.escapedText : 'any'; 182 } 183 return `${memNameStr}: ${memTypeStr}`; 184 }).join(', ') : ""; 185 return argStr; 186 }) 187 returnStr = `${returnName} <${returnArgs}>` 188 }; 189 } 190 let paramResList: ParamObj[] = []; 191 const params = member.parameters.map(param => { 192 // `${param.name}: ${param.type ? param.type : 'any'}` 193 let paramObjStr = JSON.stringify(param.name); 194 let paramStr = JSON.parse(paramObjStr).escapedText; 195 let paramTypeStr: string = 'any'; 196 197 if (param.type) { 198 let paramTypeObjStr = JSON.stringify(param.type); 199 // Logger.getInstance().debug(`paramTypeObjStr: ${paramTypeObjStr} }`); 200 paramTypeStr = param.type?.getText(sourceFile); //getParamType(param.type); 201 if (JSON.parse(paramTypeObjStr).typeName) { 202 paramTypeStr = JSON.parse(paramTypeObjStr).typeName.escapedText; 203 } 204 } 205 paramResList.push({ 206 name: paramStr, 207 type: paramTypeStr, 208 arraySize: 0, 209 arraySizeList: [] 210 }) 211 return `${paramStr}: ${paramTypeStr}` 212 }).join(', '); 213 Logger.getInstance().debug(` Method: ${methodName}, Return Type: ${returnStr}, Parameters: ${params}`); 214 classItem.functionList.push({ 215 name: methodName, 216 returns: returnStr, 217 parameters: paramResList, 218 type: '', 219 }); 220 } else if (ts.isPropertyDeclaration(member) || ts.isPropertyAssignment(member)) { 221 // 判断是否是类的成员变量 222 if ('type' in member && 'text' in member.name) { 223 let paramTypeText = member.type?.getText(sourceFile); //getParamType(member.type); 224 let parameter: ParamObj = { 225 name: member.name.text, 226 type: paramTypeText, 227 arraySize: 0, 228 arraySizeList: [] 229 } 230 classItem.variableList.push(parameter); 231 } 232 } 233 }); 234 parseRes.classes.push(classItem); 235 } catch (error) { 236 Logger.getInstance().error('Error processing node:' + error); 237 } 238 } else if (ts.isEnumDeclaration(node) && node.name) { 239 try { 240 Logger.getInstance().debug(`Enum: ${node.name.text}`); 241 let enumItem: EnumObj = { 242 name: node.name.text, 243 alias: '', 244 members: [], 245 values: [], 246 }; 247 // Logger.getInstance().debug(`Enum: ${node.name.text}, ${node.members.length}`); 248 node.members.forEach(member => { 249 const memJsonStr = JSON.stringify(member.name); 250 const memJsonObj = JSON.parse(memJsonStr); 251 // Logger.getInstance().debug(`Member: ${memJsonObj.escapedText}`) 252 if (ts.isEnumMember(member)) { 253 if (ts.isIdentifier(member.name)) { 254 enumItem.members.push(member.name.getText(sourceFile)); 255 } 256 } 257 // enumItem.members.push(memJsonObj.escapedText); 258 259 let valueText = ""; 260 let computedValue: number | undefined; 261 // 提取初始化表达式 262 if (member.initializer) { 263 valueText = member.initializer.getText(sourceFile); 264 if (ts.isCallExpression(member.initializer)) { 265 valueText = member.initializer.expression.getText(sourceFile); 266 } 267 // 编译时计算表达式值(仅限常量表达式) 268 const checker = (sourceFile as any).symbol?.parent?.checker; 269 if (checker) { 270 const type = checker.getTypeAtLocation(member.initializer); 271 computedValue = type.isNumberLiteral() ? type.value : undefined; 272 } 273 enumItem.values?.push(valueText); 274 } 275 }) 276 277 278 parseRes.enums.push(enumItem); 279 } catch (error) { 280 Logger.getInstance().error('Error processing node:' + error); 281 } 282 } else if (ts.isTypeAliasDeclaration(node) && node.name) { 283 Logger.getInstance().debug(`Type: ${node.name.text}`); 284 let typeItem: TypeObj = { 285 name: node.name.text, 286 alias: node.type?.getText(sourceFile), //getParamType(node.type), 287 members: [], 288 functions: [], 289 types: [], 290 }; 291 if (node.type && node.type.members) { 292 node.type.members.forEach(member => { 293 // 处理属性 294 if (ts.isPropertySignature(member)) { 295 typeItem.members.push({ 296 name: member.name.getText(sourceFile), 297 type: member.type?.getText(sourceFile) || "unknown", 298 arraySize: 0, 299 arraySizeList: [] 300 }); 301 } 302 // 处理方法 303 if (ts.isMethodSignature(member)) { 304 const parameters = member.parameters.map(param => ({ 305 name: param.name.getText(sourceFile), 306 type: param.type?.getText(sourceFile) || "any", 307 arraySize: 0, 308 arraySizeList: [] 309 })); 310 311 typeItem.functions.push({ 312 name: member.name.getText(sourceFile), 313 returns: member.type?.getText(sourceFile) || "void", 314 type: '', 315 parameters: parameters 316 }); 317 } 318 319 }); 320 } else if (ts.isUnionTypeNode(node.type)) { 321 // 处理联合类型 322 node.type.types.forEach(typeNode => { 323 typeItem.types.push(typeNode.getText(sourceFile)); 324 }); 325 } 326 327 parseRes.types!.push(typeItem); 328 } else if (ts.isFunctionDeclaration(node) && node.name) { 329 Logger.getInstance().debug(`Type: ${node.name.text}`); 330 const parameters = node.parameters; 331 let parames: ParamObj[] = []; 332 parameters.forEach(param => { 333 let paramName = ''; 334 if ('text' in param.name) { 335 // 参数名称,如 "v1" 336 paramName = param.name.text; 337 } 338 // 参数类型节点 339 const paramType = param.type; 340 let paramText = param.type?.getText(sourceFile); //getParamType(paramType); 341 342 Logger.getInstance().debug(` ${paramName}: ${paramText}`); 343 let parameter: ParamObj = { 344 name: paramName, 345 type: paramText, 346 arraySize: 0, 347 arraySizeList: [] 348 } 349 parames.push(parameter); 350 }); 351 352 // 获取返回值类型 353 const returnTypeNode = node.type; 354 let returnTypeText = node.type?.getText(sourceFile); //getParamType(returnTypeNode); 355 let funcItem: FuncObj = { 356 name: node.name.text, 357 returns: returnTypeText, 358 parameters: parames, 359 type: '', 360 } 361 parseRes.funcs.push(funcItem); 362 } else if (ts.isInterfaceDeclaration(node) && node.name) { 363 Logger.getInstance().debug(`structItem: ${node.name.text}, ${node.members}`); 364 let structItem: StructObj = { 365 name: node.name.text, 366 alias: '', 367 members: [], 368 functions: [] 369 }; 370 try { 371 node.members.forEach(member => { 372 // Logger.getInstance().debug(`Member: ${JSON.stringify(member)}`) 373 if (ts.isMethodDeclaration(member) && member.name) { 374 // 判断是否是方法 375 let paramTypeText = member.type?.getText(sourceFile); //getParamType(member.type); 376 let parameter: ParamObj = { 377 name: 'test', 378 type: paramTypeText, 379 arraySize: 0, 380 arraySizeList: [] 381 } 382 structItem.members.push(parameter); 383 } else if (ts.isPropertyDeclaration(member) || ts.isPropertyAssignment(member)) { 384 // 判断是否是类的成员变量 385 if ('type' in member && 'text' in member.name) { 386 let paramTypeText = member.type?.getText(sourceFile); //getParamType(member.type); 387 let parameter: ParamObj = { 388 name: member.name.text, 389 type: paramTypeText, 390 arraySize: 0, 391 arraySizeList: [] 392 } 393 structItem.members.push(parameter); 394 } 395 } else if (ts.isPropertySignature(member)) { 396 const name = member.name.getText(sourceFile); 397 const type = member.type?.getText(sourceFile) || 'any'; 398 let parameter = { 399 name: name, 400 type: type, 401 arraySize: 0, 402 arraySizeList: [] 403 }; 404 structItem.members.push(parameter); 405 } else if (ts.isMethodSignature(member)) { 406 const parameters = member.parameters.map(param => ({ 407 name: param.name.getText(sourceFile), 408 type: param.type?.getText(sourceFile) || "any", 409 arraySize: 0, 410 arraySizeList: [] 411 })); 412 413 const name = member.name.getText(sourceFile); 414 const type = member.type?.getText(sourceFile) || 'void'; 415 let funcobj = { 416 type: '', 417 name: name, 418 returns: type, 419 parameters: parameters 420 } 421 structItem.functions.push(funcobj); 422 } 423 }); 424 parseRes.structs.push(structItem); 425 } catch (error) { 426 Logger.getInstance().error('Error processing node:' + error); 427 } 428 } 429 ts.forEachChild(node, visitor); 430 } 431 432 const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest); 433 const program = ts.createProgram([filePath], {}); 434 gchecker = program.getTypeChecker(); 435 ts.forEachChild(sourceFile, visitor); 436 437 return parseRes; 438} 439 440export function parseTsFile(filePath: string): ParseObj { 441 const sourceCode = fs.readFileSync(filePath, 'utf8'); 442 return doParseTs(filePath, sourceCode); 443}