• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device 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 ts, { type CommentRange } from 'typescript';
17
18import { LogUtil } from '../utils/logUtil';
19import {
20  PropertyInfo,
21  type ApiInfo,
22  type comment,
23  type JsDocProcessorInterface,
24  MethodInfo,
25  type ModifierProcessorInterface,
26  type NodeProcessorInterface,
27  JsDocTag,
28  ApiType,
29  EnumValueInfo,
30  NamespaceEnumInfo,
31  type PropertyNode,
32  UnionTypeInfo,
33  TypeAliasInfo,
34  ClassInterfaceInfo,
35  ExportDefaultInfo,
36  ImportInfo,
37  type MethodType,
38  ParamInfo,
39  DeclareConstInfo,
40  ConstantInfo,
41  type BasicApiInfo,
42  Options,
43  JsDocInfo
44} from './typedef';
45import { StringUtils } from '../utils/StringUtils';
46
47export class CommentHelper {
48  static licenseKeyword: string = 'Copyright';
49  static referenceRegexp: RegExp = /\/\/\/\s*<reference\s*path/g;
50  static referenceCommentRegexp: RegExp = /\/\s*<reference\s*path/g;
51  static mutiCommentDelimiter: string = '/**';
52
53  /**
54   * 获取指定AST节点上的注释,若无注释返回空数组。
55   *
56   * @param node
57   * @param sourceFile
58   * @returns
59   */
60  static getNodeLeadingComments(node: ts.Node, sourceFile: ts.SourceFile): comment.CommentInfo[] {
61    try {
62      const leadingCommentRange: CommentRange[] | undefined = ts.getLeadingCommentRanges(sourceFile.getFullText(), node.getFullStart());
63      if (leadingCommentRange?.length) {
64        const parsedCommentInfos: Array<comment.CommentInfo> = [];
65        leadingCommentRange.forEach((range) => {
66          const comment = sourceFile.getFullText().slice(range.pos, range.end);
67          const commentInfo = CommentHelper.parseComment(comment, range.kind, true);
68          commentInfo.pos = range.pos;
69          commentInfo.end = range.end;
70          parsedCommentInfos.push(commentInfo);
71        });
72        return parsedCommentInfos;
73      }
74      return [];
75    } catch (error) {
76      LogUtil.d('CommentHelper', `node(kind=${node.kind}) is created in memory.`);
77      return [];
78    }
79  }
80
81  /**
82   * 将多段注释文本解析成注释对象。
83   *
84   * @param comment
85   * @returns
86   */
87  static parseComment(comment: string, commentKind: ts.CommentKind, isLeading: boolean): comment.CommentInfo {
88    const { parse } = require('comment-parser');
89    const commentInfo: comment.CommentInfo = {
90      text: comment,
91      isMultiLine: commentKind === ts.SyntaxKind.MultiLineCommentTrivia,
92      isLeading: isLeading,
93      description: '',
94      commentTags: [],
95      parsedComment: undefined,
96      pos: -1,
97      end: -1,
98      ignore: false,
99      isApiComment: false,
100      isInstruct: false
101    };
102    let commentString: string = comment;
103    let parsedComments = parse(commentString);
104    // 无法被解析的注释,可能以 /* 开头或是单行注释
105    if (parsedComments.length === 0) {
106      // 注释是 /// <reference path="" /> 或 单行注释
107      if (StringUtils.hasSubstring(commentString, this.referenceRegexp) ||
108        commentKind === ts.SyntaxKind.SingleLineCommentTrivia) {
109        commentInfo.isMultiLine = false;
110        // 注释内容需丢弃 "//"
111        const startIndex: number = 2;
112        commentInfo.text = commentString.substring(startIndex, commentString.length);
113      }
114      return commentInfo;
115    }
116    commentInfo.parsedComment = parsedComments[0];
117    commentInfo.description = parsedComments[0].description;
118    for (let i = 0; i < parsedComments[0].tags.length; i++) {
119      commentInfo.commentTags.push({
120        tag: parsedComments[0].tags[i].tag,
121        name: parsedComments[0].tags[i].name,
122        type: parsedComments[0].tags[i].type,
123        optional: parsedComments[0].tags[i].optional,
124        description: parsedComments[0].tags[i].description,
125        source: parsedComments[0].tags[i].source[0].source,
126        lineNumber: parsedComments[0].tags[i].source[0].number,
127        tokenSource: parsedComments[0].tags[i].source,
128        defaultValue: parsedComments[0].tags[i].default ? parsedComments[0].tags[i].default : undefined
129      });
130    }
131    commentInfo.isApiComment = true;
132    return commentInfo;
133  }
134}
135
136export class JsDocProcessorHelper {
137  static setSyscap(jsDocInfo: JsDocInfo, syscap?: string): void {
138    if (!syscap) {
139      return;
140    }
141    jsDocInfo.setSyscap(syscap);
142  }
143
144  static setSince(jsDocInfo: JsDocInfo, since?: string): void {
145    if (!since || isNaN(Number(since))) {
146      return;
147    }
148    jsDocInfo.setSince(Number(since));
149  }
150
151  static setIsForm(jsDocInfo: JsDocInfo): void {
152    jsDocInfo.setIsForm();
153  }
154
155  static setIsCrossPlatForm(jsDocInfo: JsDocInfo): void {
156    jsDocInfo.setIsCrossPlatForm();
157  }
158
159  static setIsSystemApi(jsDocInfo: JsDocInfo): void {
160    jsDocInfo.setIsSystemApi();
161  }
162
163  static setIsStageModelOnly(jsDocInfo: JsDocInfo): void {
164    jsDocInfo.setIsStageModelOnly();
165  }
166
167  static setIsFaModelOnly(jsDocInfo: JsDocInfo): void {
168    jsDocInfo.setIsFaModelOnly();
169  }
170
171  static setDeprecatedVersion(jsDocInfo: JsDocInfo, deprecatedVersion?: string): void {
172    if (!deprecatedVersion || isNaN(Number(deprecatedVersion))) {
173      return;
174    }
175    jsDocInfo.setDeprecatedVersion(Number(deprecatedVersion));
176  }
177
178  static setUseinstead(jsDocInfo: JsDocInfo, useinstead?: string): void {
179    if (!useinstead) {
180      return;
181    }
182    jsDocInfo.setUseinstead(useinstead);
183  }
184
185  static setPermission(jsDocInfo: JsDocInfo, permission?: string): void {
186    if (!permission) {
187      return;
188    }
189    jsDocInfo.setPermission(permission);
190  }
191
192  static addErrorCode(jsDocInfo: JsDocInfo, errorCode?: string): void {
193    if (!errorCode || isNaN(Number(errorCode))) {
194      return;
195    }
196    jsDocInfo.addErrorCode(Number(errorCode));
197  }
198
199  static setIsRequired(jsDocInfo: JsDocInfo, typeContent?: string): void {
200    if (!typeContent) {
201      return;
202    }
203    jsDocInfo.setTypeInfo(typeContent);
204  }
205
206  static setIsConstant(jsDocInfo: JsDocInfo): void {
207    jsDocInfo.setIsConstant();
208  }
209
210  static processJsDoc(comment: comment.CommentInfo): JsDocInfo {
211    const jsDocInfo: JsDocInfo = new JsDocInfo();
212    for (let i = 0; i < comment.commentTags.length; i++) {
213      const commentTag: comment.CommentTag = comment.commentTags[i];
214      const jsDocProcessor = jsDocProcessorMap.get(commentTag.tag.toLowerCase());
215      if (!jsDocProcessor) {
216        continue;
217      }
218      let option: string = commentTag.name;
219      if (commentTag.tag.toLowerCase() === JsDocTag.DEPRECATED) {
220        option = commentTag.description;
221      }
222      if (commentTag.tag.toLowerCase() === JsDocTag.TYPE) {
223        option = commentTag.type;
224      }
225      if (commentTag.tag.toLowerCase() === JsDocTag.PERMISSION) {
226        const description: string = commentTag.description;
227        const name: string = commentTag.name;
228        option = description ? `${name} ${description}` : `${name}`;
229      }
230      jsDocProcessor(jsDocInfo, option);
231    }
232    return jsDocInfo;
233  }
234
235}
236
237export class NodeProcessorHelper {
238  // 如果是字符串的话,会出现单双引号重复的情况
239  static regQuotation: RegExp = /^[\'|\"](.*)[\'|\"]$/;
240
241  static getNodeInfo(node: ts.Node, jsDocInfo: JsDocInfo, options: Options): BasicApiInfo | undefined {
242    let apiInfo: BasicApiInfo;
243    const nodeProcessor: NodeProcessorInterface | undefined = nodeProcessorMap.get(node.kind);
244    if (!nodeProcessor) {
245      return undefined;
246    }
247    apiInfo = nodeProcessor(node, jsDocInfo, options);
248    return apiInfo;
249  }
250
251  static processNode(node: ts.Node, sourceFile: ts.SourceFile, parentVersion: number = -1,
252    value: string = '-1'): BasicApiInfo[] {
253    const allCommentInfos: comment.CommentInfo[] = CommentHelper.getNodeLeadingComments(node, sourceFile);
254    const commentInfos: comment.CommentInfo[] = allCommentInfos.filter((commentInfo: comment.CommentInfo) => {
255      return commentInfo.isApiComment;
256    });
257    const apiInfos: BasicApiInfo[] = [];
258    let options: Options = new Options();
259    options.setValue(value);
260    let indexOfComment: number = 0;
261    do {
262      let jsDocInfo: JsDocInfo = new JsDocInfo();
263      if (commentInfos.length !== 0) {
264        jsDocInfo = JsDocProcessorHelper.processJsDoc(commentInfos[indexOfComment]);
265      }
266      indexOfComment++;
267      let version: number = jsDocInfo.getSince();
268      options.setIsConstant(jsDocInfo.getIsConstant());
269      if (version !== -1 && parentVersion > version) {
270        continue;
271      }
272      const apiInfo: BasicApiInfo | undefined = NodeProcessorHelper.getNodeInfo(node, jsDocInfo, options);
273      if (!apiInfo) {
274        return apiInfos;
275      }
276      const containerNodes: ts.NodeArray<ts.Node> | undefined = NodeProcessorHelper.getContainerNode(node);
277      if (containerNodes) {
278        value = '-1';
279        const containerApiInfo: NamespaceEnumInfo = apiInfo as NamespaceEnumInfo;
280        containerNodes.forEach((cNode: ts.Node) => {
281          const childApis: BasicApiInfo[] = NodeProcessorHelper.processNode(cNode, sourceFile, version, value);
282          value = NodeProcessorHelper.getEnumValue(cNode, value, childApis);
283          containerApiInfo.addChildApi(childApis);
284        });
285      }
286      apiInfos.push(apiInfo);
287    } while (indexOfComment < commentInfos.length);
288    return apiInfos;
289  }
290
291  static getEnumValue(cNode: ts.Node, value: string, childApis: BasicApiInfo[]): string {
292    if (ts.isEnumMember(cNode)) {
293      if (cNode.initializer) {
294        value = cNode.initializer.getText();
295      } else if (childApis.length !== 0) {
296        value = (childApis[0] as EnumValueInfo).value;
297      } else {
298        value = !isNaN(Number(value)) ? `${Number(value) + 1}` : value;
299      }
300    }
301    return value;
302  }
303
304  static getContainerNode(node: ts.Node): ts.NodeArray<ts.Node> | undefined {
305    if (ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)) {
306      return node.members;
307    }
308    if (ts.isTypeAliasDeclaration(node) && ts.isTypeLiteralNode(node.type)) {
309      return node.type.members;
310    }
311    if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
312      return node.body.statements;
313    }
314    return undefined;
315  }
316
317  static processExportDefault(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
318    const exportDefaultInfo: ExportDefaultInfo = new ExportDefaultInfo(ApiType.EXPORT_DEFAULT);
319    const exportDefaultNode: ts.ExportAssignment = node as ts.ExportAssignment;
320    exportDefaultInfo.setName(exportDefaultNode.expression.getText());
321    return exportDefaultInfo;
322  }
323
324  static processImportInfo(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
325    const importInfo: ImportInfo = new ImportInfo(ApiType.IMPORT);
326    const importNode: ts.ImportDeclaration = node as ts.ImportDeclaration;
327    if (importNode.importClause === undefined) {
328      return importInfo;
329    }
330    const importClause: ts.ImportClause = importNode.importClause;
331    if (importClause.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
332      importClause.namedBindings.elements.forEach((element: ts.ImportSpecifier) => {
333        importInfo.addImportValue(element.name.getText());
334      });
335    } else {
336      const value: string | undefined = importClause.name ? importClause.name.escapedText.toString() : undefined;
337      importInfo.addImportValue(value);
338    }
339    return importInfo;
340  }
341
342  static processVariableStat(node: ts.Node, jsDocInfo: JsDocInfo, options?: Options): BasicApiInfo {
343    // declareConst、常量、属性
344    const variableNode: ts.VariableStatement = node as ts.VariableStatement;
345    const REG_DECLARE_CONSTANT: RegExp = /declare\s+const/;
346    const REG_COMPONENT: RegExp = /[a-zA-Z]+(Attribute|Interface)/;
347    const declarationNode: ts.VariableDeclaration = variableNode.declarationList.declarations[0];
348    const apiName: string = declarationNode.name.getText();
349    let variableType: string = '';
350    let value: string = '';
351    if (declarationNode.type) {
352      if (ts.isLiteralTypeNode(declarationNode.type)) {
353        const typeOfLiteral: string | undefined = typeMap.get(declarationNode.type.literal.kind);
354        variableType = typeOfLiteral ? typeOfLiteral : '';
355        value = declarationNode.type.literal.getText().replace(NodeProcessorHelper.regQuotation, '$1');
356      } else {
357        variableType = declarationNode.type.getText();
358      }
359    }
360    if (REG_DECLARE_CONSTANT.test(variableNode.getText()) && REG_COMPONENT.test(variableType)) {
361      const declareConstInfo: DeclareConstInfo = new DeclareConstInfo(ApiType.DECLARE_CONST, jsDocInfo);
362      declareConstInfo.setName(apiName);
363      declareConstInfo.setType(variableType);
364      return declareConstInfo;
365    } else if ((options && options.isConstant) || declarationNode.initializer) {
366      const constantInfo: ConstantInfo = new ConstantInfo(ApiType.CONSTANT, jsDocInfo);
367      constantInfo.setName(apiName);
368      if (declarationNode.initializer) {
369        value = declarationNode.initializer.getText().replace(NodeProcessorHelper.regQuotation, '$1');
370        const typeOfLiteral: string | undefined = typeMap.get(declarationNode.initializer.kind);
371        variableType = typeOfLiteral ? typeOfLiteral : '';
372      }
373      constantInfo.setValue(value);
374      constantInfo.setType(variableType);
375      return constantInfo;
376    } else {
377      return NodeProcessorHelper.processVaribleProperty(jsDocInfo, apiName, variableType, variableNode);
378    }
379  }
380
381  static processVaribleProperty(jsDocInfo: JsDocInfo, apiName: string, variableType: string,
382    variableNode: ts.VariableStatement): PropertyInfo {
383    const propertyInfo: PropertyInfo = new PropertyInfo(ApiType.PROPERTY, jsDocInfo);
384    propertyInfo.setName(apiName);
385    propertyInfo.setType(variableType);
386    propertyInfo.setIsRequired(true);
387    if (StringUtils.hasSubstring(variableNode.getText(), 'const')) {
388      propertyInfo.setIsReadOnly();
389    }
390    if (jsDocInfo.getTypeInfo() !== '') {
391      const typeContent: string = jsDocInfo.getTypeInfo();
392      const isRequired: boolean = !/\?/.test(typeContent);
393      propertyInfo.setIsRequired(isRequired);
394      propertyInfo.setType(typeContent.replace(/^[\?*\(](.*)[\)]$/, '$1'));
395    }
396    return propertyInfo;
397  }
398
399  static isEventSubscription(methodName: string, index: number): boolean {
400    if (methodName && eventSubscriptionSet.has(methodName) && index === 0) {
401      return true;
402    }
403    return false;
404  }
405
406  static processParam(jsDocInfo: JsDocInfo, param: ts.ParameterDeclaration,
407    index: number, methodInfo: MethodInfo): ParamInfo {
408    const paramInfo: ParamInfo = new ParamInfo(ApiType.PARAM, jsDocInfo);
409    paramInfo.setName(param.name.getText());
410    paramInfo.setIsRequired(!param.questionToken ? true : false);
411    let paramType: string | undefined = undefined;
412    if (param.type === undefined) {
413      paramInfo.setType(paramType);
414      return paramInfo;
415    }
416    paramType = param.type.getText();
417    let typeMapValue: string | undefined = undefined;
418    if (ts.isLiteralTypeNode(param.type)) {
419      typeMapValue = typeMap.get(param.type.literal.kind);
420    }
421    if (typeMapValue || ts.isUnionTypeNode(param.type)) {
422      let methodName: string = methodInfo.name;
423      if (NodeProcessorHelper.isEventSubscription(methodName, index)) {
424        methodName = `${methodName}(${paramType})`;
425        methodInfo.setName(methodName);
426      }
427      paramType = 'string';
428    }
429    paramInfo.setType(paramType);
430    return paramInfo;
431  }
432
433  static processMethod(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
434    const methodNode: MethodType = node as MethodType;
435    const methodInfo: MethodInfo = new MethodInfo(ApiType.METHOD, jsDocInfo);
436    let methodName: string | undefined = methodNode.name ? methodNode.name.getText() : undefined;
437    methodInfo.setName(methodName);
438    const callForm: string = methodNode.getText().replace(/export\s+|declare\s+|function\s+|\r\n|\;/g, '');
439    methodInfo.setCallForm(callForm);
440    if (methodNode.type && ts.SyntaxKind.VoidKeyword !== methodNode.type.kind) {
441      methodInfo.setReturnValue(methodNode.type.getText());
442    }
443    for (let i = 0; i < methodNode.parameters.length; i++) {
444      const param: ts.ParameterDeclaration = methodNode.parameters[i];
445      const paramInfo: ParamInfo = NodeProcessorHelper.processParam(jsDocInfo, param, i, methodInfo);
446      methodInfo.addParam(paramInfo);
447    }
448    if (!ts.isCallSignatureDeclaration(methodNode) && methodNode.modifiers) {
449      methodNode.modifiers.forEach((modifier: ts.ModifierLike) => {
450        const setModifier: ModifierProcessorInterface | undefined = modifierProcessorMap.get(modifier.kind);
451        setModifier ? setModifier(methodInfo) : undefined;
452      });
453    }
454    return methodInfo;
455  }
456
457  static processPropertySigAndDec(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
458    const propertyNode: PropertyNode = node as PropertyNode;
459    const propertyInfo: PropertyInfo = new PropertyInfo(ApiType.PROPERTY, jsDocInfo);
460    propertyInfo.setName(propertyNode.name.getText());
461    propertyInfo.setIsRequired(!propertyNode.questionToken ? true : false);
462    propertyNode.type ? propertyInfo.setType(propertyNode.type.getText()) : undefined;
463    if (jsDocInfo.getTypeInfo() !== '') {
464      const typeContent: string = jsDocInfo.getTypeInfo();
465      const isRequired: boolean = !/\?/.test(typeContent);
466      propertyInfo.setIsRequired(isRequired);
467      propertyInfo.setType(typeContent.replace(/\?/, '').replace(/^[\(](.*)[\)]$/, '$1'));
468    }
469    if (!propertyNode.modifiers) {
470      return propertyInfo;
471    }
472    propertyNode.modifiers.forEach((modifier: ts.ModifierLike) => {
473      const setModifier: ModifierProcessorInterface | undefined = modifierProcessorMap.get(modifier.kind);
474      setModifier ? setModifier(propertyInfo) : undefined;
475    });
476    return propertyInfo;
477  }
478
479  static processEnumValue(node: ts.Node, jsDocInfo: JsDocInfo, options?: Options): BasicApiInfo {
480    const enumValueNode: ts.EnumMember = node as ts.EnumMember;
481    const enumValueInfo: EnumValueInfo = new EnumValueInfo(ApiType.ENUM_VALUE, jsDocInfo);
482    enumValueInfo.setName(enumValueNode.name.getText());
483    let value: string = '';
484    if (options && options.value && !isNaN(Number(options.value))) {
485      value = `${Number(options.value) + 1}`;
486    }
487    if (enumValueNode.initializer) {
488      value = enumValueNode.initializer.getText().replace(NodeProcessorHelper.regQuotation, '$1');
489    }
490    enumValueInfo.setValue(value);
491    return enumValueInfo;
492  }
493
494  static processEnum(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
495    const enumNode: ts.EnumDeclaration = node as ts.EnumDeclaration;
496    const enumInfo: NamespaceEnumInfo = new NamespaceEnumInfo(ApiType.ENUM, jsDocInfo);
497    enumInfo.setName(enumNode.name.getText());
498    return enumInfo;
499  }
500
501  static processTypeAlias(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
502    const typeAliasNode: ts.TypeAliasDeclaration = node as ts.TypeAliasDeclaration;
503    const apiName: string = typeAliasNode.name.getText();
504    let apiInfo: ApiInfo;
505    if (typeAliasNode.type.kind === ts.SyntaxKind.UnionType) {
506      apiInfo = NodeProcessorHelper.processUnionType((typeAliasNode.type as ts.UnionTypeNode), apiName, jsDocInfo);
507    } else if (typeAliasNode.type.kind === ts.SyntaxKind.TypeReference) {
508      apiInfo = NodeProcessorHelper.processSecondModule(typeAliasNode, apiName, jsDocInfo);
509    } else {
510      apiInfo = NodeProcessorHelper.processTypeInterface(node, jsDocInfo);
511    }
512    return apiInfo;
513  }
514
515  static processTypeInterface(node: ts.Node, jsDocInfo: JsDocInfo): ApiInfo {
516    const typeAliasNode: ts.TypeAliasDeclaration = node as ts.TypeAliasDeclaration;
517    const interfaceInfo: ClassInterfaceInfo = new ClassInterfaceInfo(ApiType.INTERFACE, jsDocInfo);
518    interfaceInfo.setName(typeAliasNode.name.getText());
519    return interfaceInfo;
520  }
521
522  static processSecondModule(node: ts.TypeAliasDeclaration, name: string, jsDocInfo: JsDocInfo): TypeAliasInfo {
523    const typeAliasInfo: TypeAliasInfo = new TypeAliasInfo(ApiType.TYPE_ALIAS, jsDocInfo);
524    typeAliasInfo.setName(name);
525    typeAliasInfo.setType(node.type.getText());
526    return typeAliasInfo;
527  }
528
529  static processUnionType(node: ts.UnionTypeNode, name: string, jsDocInfo: JsDocInfo): UnionTypeInfo {
530    const unionTypeInfo: UnionTypeInfo = new UnionTypeInfo(ApiType.UNIONTYPE, jsDocInfo);
531    const unionTypeNode: ts.UnionTypeNode = node as ts.UnionTypeNode;
532    unionTypeInfo.setName(name);
533    unionTypeNode.types.forEach((type: ts.TypeNode) => {
534      unionTypeInfo.addValueRange(type.getText().replace(NodeProcessorHelper.regQuotation, '$1'));
535    });
536    return unionTypeInfo;
537  }
538
539  static processClass(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
540    const classDeclaration: ts.ClassDeclaration = node as ts.ClassDeclaration;
541    const classInfo: ClassInterfaceInfo = new ClassInterfaceInfo(ApiType.CLASS, jsDocInfo);
542    classInfo.setName(classDeclaration.name?.getText());
543    if (classDeclaration.heritageClauses === undefined) {
544      return classInfo;
545    }
546    const parentClasses: string[] = [];
547    classDeclaration.heritageClauses.forEach((value: ts.HeritageClause) => {
548      value.types.forEach((value: ts.ExpressionWithTypeArguments) => {
549        parentClasses.push(value.expression.getText());
550      });
551    });
552    classInfo.setParentClasses(parentClasses);
553    return classInfo;
554  }
555
556  static processInterface(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
557    const interfaceDeclaration: ts.InterfaceDeclaration = node as ts.InterfaceDeclaration;
558    const interfaceInfo: ClassInterfaceInfo = new ClassInterfaceInfo(ApiType.INTERFACE, jsDocInfo);
559    interfaceInfo.setName(interfaceDeclaration.name.getText());
560    if (interfaceDeclaration.heritageClauses === undefined) {
561      return interfaceInfo;
562    }
563    const parentClasses: string[] = [];
564    interfaceDeclaration.heritageClauses.forEach((value: ts.HeritageClause) => {
565      value.types.forEach((value: ts.ExpressionWithTypeArguments) => {
566        parentClasses.push(value.expression.getText());
567      });
568    });
569    interfaceInfo.setParentClasses(parentClasses);
570    return interfaceInfo;
571  }
572
573  static processNamespace(node: ts.Node, jsDocInfo: JsDocInfo): BasicApiInfo {
574    const moduleDeclaration: ts.ModuleDeclaration = node as ts.ModuleDeclaration;
575    const namespaceEnumInfo: NamespaceEnumInfo = new NamespaceEnumInfo(ApiType.NAMESPACE, jsDocInfo);
576    namespaceEnumInfo.setName(moduleDeclaration.name.getText());
577    return namespaceEnumInfo;
578  }
579}
580
581export class ModifierHelper {
582  static setIsStatic(apiInfo: PropertyInfo | MethodInfo): void {
583    apiInfo.setIsStatic();
584  }
585
586  static setIsReadonly(apiInfo: PropertyInfo): void {
587    apiInfo.setIsReadOnly();
588  }
589}
590
591const jsDocProcessorMap: Map<string, JsDocProcessorInterface> = new Map([
592  [JsDocTag.SYSCAP, JsDocProcessorHelper.setSyscap],
593  [JsDocTag.SINCE, JsDocProcessorHelper.setSince],
594  [JsDocTag.FORM, JsDocProcessorHelper.setIsForm],
595  [JsDocTag.CROSS_PLAT_FORM, JsDocProcessorHelper.setIsCrossPlatForm],
596  [JsDocTag.SYSTEM_API, JsDocProcessorHelper.setIsSystemApi],
597  [JsDocTag.STAGE_MODEL_ONLY, JsDocProcessorHelper.setIsStageModelOnly],
598  [JsDocTag.FA_MODEL_ONLY, JsDocProcessorHelper.setIsFaModelOnly],
599  [JsDocTag.DEPRECATED, JsDocProcessorHelper.setDeprecatedVersion],
600  [JsDocTag.USEINSTEAD, JsDocProcessorHelper.setUseinstead],
601  [JsDocTag.TYPE, JsDocProcessorHelper.setIsRequired],
602  [JsDocTag.PERMISSION, JsDocProcessorHelper.setPermission],
603  [JsDocTag.THROWS, JsDocProcessorHelper.addErrorCode],
604  [JsDocTag.CONSTANT, JsDocProcessorHelper.setIsConstant],
605]);
606
607const modifierProcessorMap: Map<ts.SyntaxKind, ModifierProcessorInterface> = new Map([
608  [ts.SyntaxKind.ConstKeyword, ModifierHelper.setIsReadonly],
609  [ts.SyntaxKind.ReadonlyKeyword, ModifierHelper.setIsReadonly],
610  [ts.SyntaxKind.StaticKeyword, ModifierHelper.setIsStatic]
611]);
612
613const nodeProcessorMap: Map<ts.SyntaxKind, NodeProcessorInterface> = new Map([
614  [ts.SyntaxKind.ExportAssignment, NodeProcessorHelper.processExportDefault],
615  [ts.SyntaxKind.ImportDeclaration, NodeProcessorHelper.processImportInfo],
616  [ts.SyntaxKind.VariableStatement, NodeProcessorHelper.processVariableStat],
617  [ts.SyntaxKind.MethodDeclaration, NodeProcessorHelper.processMethod],
618  [ts.SyntaxKind.MethodSignature, NodeProcessorHelper.processMethod],
619  [ts.SyntaxKind.FunctionDeclaration, NodeProcessorHelper.processMethod],
620  [ts.SyntaxKind.CallSignature, NodeProcessorHelper.processMethod],
621  [ts.SyntaxKind.PropertyDeclaration, NodeProcessorHelper.processPropertySigAndDec],
622  [ts.SyntaxKind.PropertySignature, NodeProcessorHelper.processPropertySigAndDec],
623  [ts.SyntaxKind.EnumMember, NodeProcessorHelper.processEnumValue],
624  [ts.SyntaxKind.EnumDeclaration, NodeProcessorHelper.processEnum],
625  [ts.SyntaxKind.TypeAliasDeclaration, NodeProcessorHelper.processTypeAlias],
626  [ts.SyntaxKind.ClassDeclaration, NodeProcessorHelper.processClass],
627  [ts.SyntaxKind.InterfaceDeclaration, NodeProcessorHelper.processInterface],
628  [ts.SyntaxKind.ModuleDeclaration, NodeProcessorHelper.processNamespace]
629]);
630
631const typeMap: Map<ts.SyntaxKind, string> = new Map([
632  [ts.SyntaxKind.StringLiteral, 'string'],
633  [ts.SyntaxKind.NumericLiteral, 'number']
634]);
635
636const eventSubscriptionSet: Set<string> = new Set([
637  'on',
638  'off',
639  'once',
640  'emit'
641]);