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]);