• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}