• 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                })
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}