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 }) 84 return `(${nameObj.escapedText}:${kindStr})`; 85 }); 86 } 87 // 处理其他类型 88 return [JSON.stringify(typeNode)]; 89 } catch (error) { 90 Logger.getInstance().error('Error processing node:' + error); 91 } 92 return []; 93} 94 95export function getParamType(paramType: any) { 96 if (paramType === undefined) { 97 return 'void'; 98 } 99 // 类型为 number 100 let paramText = paramType.kind === NUMBER_TYPE ? 'number' : 101 // 类型为 string 102 paramType.kind === STRING_TYPE ? 'string' : 103 // 类型为 boolean 104 paramType.kind === BOOLEAN_TYPE ? 'boolean' : 105 paramType.kind === VOID_TYPE ? 'void' : 106 // 默认any类型 107 'any'; 108 if (paramType.kind === OBJECT_TYPE) { 109 const type = paramType.typeName.escapedText; 110 if (paramType.typeArguments) { 111 const subType = paramType.typeArguments[0].kind === NUMBER_TYPE ? 'number' : 112 paramType.typeArguments[0].kind === STRING_TYPE ? 'string' : 113 paramType.typeArguments[0].kind === BOOLEAN_TYPE ? 'boolean' : 'any'; 114 if (type === 'Array') { 115 paramText = 'Array<' + subType + '>'; 116 } 117 } else { 118 return type 119 } 120 } 121 if (paramType.kind === ARRAY_TYPE) { 122 const subType = paramType.elementType.kind === NUMBER_TYPE ? 'number' : 123 paramType.elementType.kind === STRING_TYPE ? 'string' : 124 paramType.elementType.kind === BOOLEAN_TYPE ? 'boolean' : 125 'any'; 126 paramText = 'Array<' + subType + '>'; 127 } 128 return paramText; 129} 130 131export function doParseTs(filePath: string, sourceCode: string): ParseObj { 132 let parseRes: ParseObj = { 133 enums: [], 134 unions: [], 135 structs: [], 136 classes: [], 137 funcs: [], 138 types: [], 139 } 140 function visitor(node: ts.Node) { 141 if (ts.isClassDeclaration(node) && node.name) { 142 Logger.getInstance().debug(`Class: ${node.name.text}, ${node.members}`); 143 let classItem: ClassObj = { 144 name: node.name.text, 145 alias: '', 146 functionList: [], 147 variableList: [] 148 }; 149 try { 150 node.members.forEach(member => { 151 // Logger.getInstance().debug(`Member: ${JSON.stringify(member)}`) 152 if (ts.isMethodDeclaration(member) && member.name) { 153 const methodstr = member.name ? JSON.stringify(member.name) : 'noname'; 154 const methodObject = JSON.parse(methodstr); 155 const methodName = methodObject.escapedText; 156 Logger.getInstance().debug(`memberName: ${methodName} `); 157 let returnStr = 'void'; 158 if (member.type) { 159 let returnObjStr = JSON.stringify(member.type); 160 // Logger.getInstance().debug(`returnObjStr: ${returnObjStr} `); 161 let returnObj = JSON.parse(returnObjStr); 162 returnStr = getParamType(member.type); 163 if (returnObj.typeName) { 164 let returnNameStr = JSON.stringify(returnObj.typeName); 165 let returnName = JSON.parse(returnNameStr).escapedText; 166 let returnArgsStr = JSON.stringify(returnObj.typeArguments); 167 let returnArgsObj = JSON.parse(returnArgsStr); 168 const returnArgs = returnArgsObj.map((argItem: TypeArguments) => { 169 let argStr = argItem.members.map((memItem: MemberObj) => { 170 let memNameStr = ''; 171 let memTypeStr = 'void'; 172 if (memItem.name) { 173 memNameStr = memItem.name.escapedText; 174 } 175 if (memItem.type) { 176 memTypeStr = memItem.type.escapedText ? memItem.type.escapedText : 'any'; 177 } 178 return `${memNameStr}: ${memTypeStr}`; 179 }).join(', '); 180 return argStr; 181 }) 182 returnStr = `${returnName} <${returnArgs}>` 183 }; 184 } 185 let paramResList: ParamObj[] = []; 186 const params = member.parameters.map(param => { 187 // `${param.name}: ${param.type ? param.type : 'any'}` 188 let paramObjStr = JSON.stringify(param.name); 189 let paramStr = JSON.parse(paramObjStr).escapedText; 190 let paramTypeStr: string = 'any'; 191 192 if (param.type) { 193 let paramTypeObjStr = JSON.stringify(param.type); 194 // Logger.getInstance().debug(`paramTypeObjStr: ${paramTypeObjStr} }`); 195 paramTypeStr = getParamType(param.type); 196 if (JSON.parse(paramTypeObjStr).typeName) { 197 paramTypeStr = JSON.parse(paramTypeObjStr).typeName.escapedText; 198 } 199 } 200 paramResList.push({ 201 name: paramStr, 202 type: paramTypeStr, 203 arraySize: 0, 204 arraySizeList: [] 205 }) 206 return `${paramStr}: ${paramTypeStr}` 207 }).join(', '); 208 Logger.getInstance().debug(` Method: ${methodName}, Return Type: ${returnStr}, Parameters: ${params}`); 209 classItem.functionList.push({ 210 name: methodName, 211 returns: returnStr, 212 parameters: paramResList, 213 type: '', 214 }); 215 } else if (ts.isPropertyDeclaration(member) || ts.isPropertyAssignment(member)) { 216 // 判断是否是类的成员变量 217 if ('type' in member && 'text' in member.name) { 218 let paramTypeText = getParamType(member.type); 219 let parameter: ParamObj = { 220 name: member.name.text, 221 type: paramTypeText, 222 arraySize: 0, 223 arraySizeList: [] 224 } 225 classItem.variableList.push(parameter); 226 } 227 } 228 }); 229 parseRes.classes.push(classItem); 230 } catch (error) { 231 Logger.getInstance().error('Error processing node:' + error); 232 } 233 } else if (ts.isEnumDeclaration(node) && node.name) { 234 try { 235 Logger.getInstance().debug(`Enum: ${node.name.text}`); 236 let enumItem: EnumObj = { 237 name: node.name.text, 238 alias: '', 239 members: [], 240 values: [], 241 }; 242 // Logger.getInstance().debug(`Enum: ${node.name.text}, ${node.members.length}`); 243 node.members.forEach(member => { 244 const memJsonStr = JSON.stringify(member.name); 245 const memJsonObj = JSON.parse(memJsonStr); 246 // Logger.getInstance().debug(`Member: ${memJsonObj.escapedText}`) 247 if (ts.isEnumMember(member)) { 248 if (ts.isIdentifier(member.name)) { 249 enumItem.members.push(member.name.getText(sourceFile)); 250 } 251 } 252 // enumItem.members.push(memJsonObj.escapedText); 253 254 let valueText = ""; 255 let computedValue: number | undefined; 256 // 提取初始化表达式 257 if (member.initializer) { 258 valueText = member.initializer.getText(sourceFile); 259 if (ts.isCallExpression(member.initializer)) { 260 valueText = member.initializer.expression.getText(sourceFile); 261 } 262 // 编译时计算表达式值(仅限常量表达式) 263 const checker = (sourceFile as any).symbol?.parent?.checker; 264 if (checker) { 265 const type = checker.getTypeAtLocation(member.initializer); 266 computedValue = type.isNumberLiteral() ? type.value : undefined; 267 } 268 enumItem.values?.push(valueText); 269 } 270 }) 271 272 273 parseRes.enums.push(enumItem); 274 } catch (error) { 275 Logger.getInstance().error('Error processing node:' + error); 276 } 277 } else if (ts.isTypeAliasDeclaration(node) && node.name) { 278 Logger.getInstance().debug(`Type: ${node.name.text}`); 279 let typeItem: TypeObj = { 280 name: node.name.text, 281 alias: getParamType(node.type), 282 members: [], 283 functions: [], 284 types: [], 285 }; 286 if (node.type && node.type.members) { 287 node.type.members.forEach(member => { 288 // 处理属性 289 if (ts.isPropertySignature(member)) { 290 typeItem.members.push({ 291 name: member.name.getText(sourceFile), 292 type: member.type?.getText(sourceFile) || "unknown", 293 arraySize: 0, 294 arraySizeList: [] 295 }); 296 } 297 // 处理方法 298 if (ts.isMethodSignature(member)) { 299 const parameters = member.parameters.map(param => ({ 300 name: param.name.getText(sourceFile), 301 type: param.type?.getText(sourceFile) || "any", 302 arraySize: 0, 303 arraySizeList: [] 304 })); 305 306 typeItem.functions.push({ 307 name: member.name.getText(sourceFile), 308 returns: member.type?.getText(sourceFile) || "void", 309 type: '', 310 parameters: parameters 311 }); 312 } 313 314 }); 315 } else if (ts.isUnionTypeNode(node.type)) { 316 // 处理联合类型 317 node.type.types.forEach(typeNode => { 318 typeItem.types.push(typeNode.getText(sourceFile)); 319 }); 320 } 321 322 parseRes.types!.push(typeItem); 323 } else if (ts.isFunctionDeclaration(node) && node.name) { 324 Logger.getInstance().debug(`Type: ${node.name.text}`); 325 const parameters = node.parameters; 326 let parames: ParamObj[] = []; 327 parameters.forEach(param => { 328 let paramName = ''; 329 if ('text' in param.name) { 330 // 参数名称,如 "v1" 331 paramName = param.name.text; 332 } 333 // 参数类型节点 334 const paramType = param.type; 335 let paramText = getParamType(paramType); 336 337 Logger.getInstance().debug(` ${paramName}: ${paramText}`); 338 let parameter: ParamObj = { 339 name: paramName, 340 type: paramText, 341 arraySize: 0, 342 arraySizeList: [] 343 } 344 parames.push(parameter); 345 }); 346 347 // 获取返回值类型 348 const returnTypeNode = node.type; 349 let returnTypeText = getParamType(returnTypeNode); 350 let funcItem: FuncObj = { 351 name: node.name.text, 352 returns: returnTypeText, 353 parameters: parames, 354 type: '', 355 } 356 parseRes.funcs.push(funcItem); 357 } else if (ts.isInterfaceDeclaration(node) && node.name) { 358 Logger.getInstance().debug(`structItem: ${node.name.text}, ${node.members}`); 359 let structItem: StructObj = { 360 name: node.name.text, 361 alias: '', 362 members: [], 363 functions: [] 364 }; 365 try { 366 node.members.forEach(member => { 367 // Logger.getInstance().debug(`Member: ${JSON.stringify(member)}`) 368 if (ts.isMethodDeclaration(member) && member.name) { 369 // 判断是否是方法 370 let paramTypeText = getParamType(member.type); 371 let parameter: ParamObj = { 372 name: 'test', 373 type: paramTypeText, 374 arraySize: 0, 375 arraySizeList: [] 376 } 377 structItem.members.push(parameter); 378 } else if (ts.isPropertyDeclaration(member) || ts.isPropertyAssignment(member)) { 379 // 判断是否是类的成员变量 380 if ('type' in member && 'text' in member.name) { 381 let paramTypeText = getParamType(member.type); 382 let parameter: ParamObj = { 383 name: member.name.text, 384 type: paramTypeText, 385 arraySize: 0, 386 arraySizeList: [] 387 } 388 structItem.members.push(parameter); 389 } 390 } else if (ts.isPropertySignature(member)) { 391 const name = member.name.getText(sourceFile); 392 const type = member.type?.getText(sourceFile) || 'any'; 393 let parameter = { 394 name: name, 395 type: type, 396 arraySize: 0, 397 arraySizeList: [] 398 }; 399 structItem.members.push(parameter); 400 } else if (ts.isMethodSignature(member)) { 401 const parameters = member.parameters.map(param => ({ 402 name: param.name.getText(sourceFile), 403 type: param.type?.getText(sourceFile) || "any", 404 arraySize: 0, 405 arraySizeList: [] 406 })); 407 408 const name = member.name.getText(sourceFile); 409 const type = member.type?.getText(sourceFile) || 'void'; 410 let funcobj = { 411 type: '', 412 name: name, 413 returns: type, 414 parameters: parameters 415 } 416 structItem.functions.push(funcobj); 417 } 418 }); 419 parseRes.structs.push(structItem); 420 } catch (error) { 421 Logger.getInstance().error('Error processing node:' + error); 422 } 423 } 424 ts.forEachChild(node, visitor); 425 } 426 427 const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest); 428 const program = ts.createProgram([filePath], {}); 429 gchecker = program.getTypeChecker(); 430 ts.forEachChild(sourceFile, visitor); 431 432 return parseRes; 433} 434 435export function parseTsFile(filePath: string): ParseObj { 436 const sourceCode = fs.readFileSync(filePath, 'utf8'); 437 return doParseTs(filePath, sourceCode); 438}