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 16const ts = require('typescript'); 17 18/** 19 * 添加API信息到 apiMap 20 * 21 * @param {Map} apiMap 22 * @param {ApiDigestInfo} apiDigestInfo 23 */ 24function addApiDigestInfo(apiMap, apiDigestInfo) { 25 addApiInfo(apiMap, apiDigestInfo.getPackageName(), 26 apiDigestInfo.getClassName(), apiDigestInfo.getApiName(), 27 apiDigestInfo.getApiSignature(), apiDigestInfo); 28} 29 30/** 31 * 将API摘要信息如以下数据格式保存 32 * @example 33 * { packageName : Map } 34 * | 35 * { className : { type: ApiDigestInfo, children: Map } } 36 * | 37 * { apiName : Map } 38 * | 39 * { signature : ApiDigestInfo[] } 40 * 41 * @param {Map} apiMap API 摘要集合 42 * @param {string} packageName 包名 43 * @param {string} className 类型名 44 * @param {string} apiSignature api节点签名 45 * @param {ApiDigestInfo} api api摘要信息 46 */ 47function addApiInfo(apiMap, packageName, className, apiName, apiSignature, api) { 48 let dtsMap = apiMap.get(packageName); 49 // 集合中没有该文件数据 50 if (!dtsMap) { 51 dtsMap = new Map(); 52 apiMap.set(packageName, dtsMap); 53 } 54 // 类数据 55 let classApi = dtsMap.get(className); 56 if (!classApi) { 57 classApi = { 58 children: new Map(), 59 }; 60 dtsMap.set(className, classApi); 61 } 62 63 if (isTypeDeclaration(api) && !classApi.type) { 64 classApi.type = api; 65 return; 66 } 67 68 // api 映射表 69 let apiNameMap = classApi.children.get(apiName); 70 if (!apiNameMap) { 71 apiNameMap = new Map(); 72 classApi.children.set(apiName, apiNameMap); 73 } 74 75 let apiArray = apiNameMap.get(apiSignature); 76 if (!apiArray) { 77 apiArray = []; 78 apiNameMap.set(apiSignature, apiArray); 79 } 80 apiArray.push(api); 81} 82 83function isTypeDeclaration(api) { 84 const types = [ApiType.ClassType.code, ApiType.InterfaceType.code, ApiType.EnumType.code, ApiType.NamespaceType.code, ApiType.SourceFile.code]; 85 return types.includes(api.getApiType().code); 86} 87 88/** 89 * 获取AST节点的签名信息, 它由所有子节点非空字符串拼接而成,具有唯一性。 90 * 类型的签名由 包名+类型名来确认唯一性。 91 * 92 * @param {ts.Node} astNode 93 * @param {string} packageName 94 * @param {string} className 95 * @returns {string} 96 */ 97function getNodeSignature(astNode, packageName, className) { 98 if (ts.isInterfaceDeclaration(astNode) || ts.isClassDeclaration(astNode) || 99 ts.isModuleDeclaration(astNode) || ts.isEnumDeclaration(astNode) || ts.isSourceFile(astNode)) { 100 return `${packageName}#${className}`; 101 } 102 let signature = ''; 103 if (astNode.getChildCount() > 0) { 104 astNode.forEachChild((childNode) => { 105 signature += `#${getNodeSignature(childNode)}`; 106 }); 107 } else { 108 signature = ts.isIdentifier(astNode) ? astNode.text.replace(/\'|\"/g, '') : astNode.getText().replace(/\'|\"/g, ''); 109 } 110 return signature; 111} 112 113class ApiDigestInfo { 114 constructor() { 115 } 116 117 /** 118 * 设置包名,包名为d.ts文件与SDK根目录的相对路径 119 * 120 * @param {string} packageName 121 * @returns {Object} 122 */ 123 setPackageName(packageName) { 124 this.packageName = packageName; 125 return this; 126 } 127 128 getPackageName() { 129 return this.packageName; 130 } 131 132 /** 133 * 类型名称,如 class, enum, interface, namespace 名字 134 * 135 * @param {string} className 136 * @returns {Object} 137 */ 138 setClassName(className) { 139 this.className = className; 140 return this; 141 } 142 143 getClassName() { 144 if (this.className) { 145 return this.className; 146 } 147 let parentInfo = this.parent; 148 while (parentInfo) { 149 if (!parentInfo.className) { 150 parentInfo = parentInfo.parent; 151 } else { 152 return parentInfo.className; 153 } 154 } 155 return ''; 156 } 157 158 /** 159 * API的类型,如方法,属性,枚举等 160 * 161 * @param {ApiType} apiType 162 * @returns {Object} 163 */ 164 setApiType(apiType) { 165 this.apiType = apiType; 166 return this; 167 } 168 169 getApiType() { 170 return this.apiType; 171 } 172 173 /** 174 * API原始的文本 175 * 176 * @param {string} rawText 177 * @returns {Object} 178 */ 179 setRawText(rawText) { 180 this.rawText = rawText; 181 return this; 182 } 183 184 getRawText() { 185 return this.rawText; 186 } 187 188 /** 189 * API所属的d.ts文件路径 190 * 191 * @param {string} path 192 * @returns {Object} 193 */ 194 setPath(path) { 195 this.path = path; 196 return this; 197 } 198 199 getPath() { 200 return this.path; 201 } 202 203 204 205 /** 206 * API AST节点对象 207 * 208 * @param {ts.Node} node 209 * @returns {Object} 210 */ 211 setAstNode(node) { 212 this.node = node; 213 return this; 214 } 215 216 getAstNode() { 217 return this.node; 218 } 219 220 /** 221 * API的JSDoc信息 222 * 223 * @param {Object} jsdoc 224 * @returns {Object} 225 */ 226 setJSdoc(jsdoc) { 227 this.jsdoc = jsdoc; 228 return this; 229 } 230 231 setJSDocTagItem(jsdocTagItem) { 232 this.jsdocTagItem = jsdocTagItem; 233 } 234 235 getJSDocTagItem() { 236 return this.jsdocTagItem; 237 } 238 239 /** 240 * AST节点的签名信息,可以确保在一个模块中的唯一性 241 * 242 * @param {ts.Node} astNode 243 * @param {string} packageName 244 * @param {string} className 245 * @returns {Object} 246 */ 247 setApiSignature(astNode, packageName, className) { 248 this.signature = getNodeSignature(astNode, packageName, className); 249 return this; 250 } 251 252 getApiSignature() { 253 return this.signature; 254 } 255 256 setApiName(apiName) { 257 this.apiName = apiName; 258 return this; 259 } 260 261 getApiName() { 262 return this.apiName; 263 } 264 265 /** 266 * API 父节点摘要信息,可以确认某个节点的空间位置。 267 * 268 * @param {Object} digestInfo 269 * @returns {Object} 270 */ 271 setParent(digestInfo) { 272 this.parent = digestInfo; 273 return this; 274 } 275 276 getParent() { 277 return this.parent; 278 } 279 280 setSyscap(syscap) { 281 this.syscap = syscap; 282 return this; 283 } 284} 285 286const ApiType = { 287 EnumType: { name: '枚举', code: 0 }, 288 EnumMember: { name: '枚举成员', code: 1 }, 289 ClassType: { name: '类', code: 2 }, 290 ClassProperty: { name: '类属性', code: 3 }, 291 ClassMethod: { name: '类方法', code: 4 }, 292 InterfaceType: { name: '接口', code: 5 }, 293 InterfaceMethod: { name: '接口方法', code: 6 }, 294 InterfaceProperty: { name: '接口属性', code: 7 }, 295 ExportType: { name: '类型导出', code: 8 }, 296 FunctionType: { name: '方法', code: 9 }, 297 NamespaceType: { name: '命名空间', code: 10 }, 298 TypeAliasDeclaration: { name: '类型成员', code: 11 }, 299 Constructor: { name: '构造器', code: 12 }, 300 CallSignature: { name: '类方法', code: 13 }, 301 SourceFile: { name: 'sourfile', code: 14 } 302}; 303 304exports.ApiDigestInfo = ApiDigestInfo; 305exports.ApiType = ApiType; 306exports.apiDataHelper = { 307 addApiInfo: addApiInfo, 308 addApiDigestInfo: addApiDigestInfo, 309 getNodeSignature: getNodeSignature, 310};