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 fs = require('fs');
17const ts = require('typescript');
18const ExcelJS = require('exceljs');
19const path = require('path');
20const { sys } = require('typescript');
21// 是否统计type类型API的开关,true表示统计
22const typeCollection = true;
23// 是否不合并同名函数的开关,true不合并
24const isNotMerge = true;
25
26// 解析文件 文本内容并将结果push到列表中并去重
27function parse(files) {
28  const api = [];
29  const exportApi = [];
30  const returnDeclarationArr = new Set([]);
31  const hash = new Set([]);
32  const fileContentList = [];
33  files.forEach(file => {
34    let fileContent = fs.readFileSync(file, 'utf-8');
35    fileContentList.push({
36      fileName: path.basename(file).replace(/.d.ts$/g, '.ts'),
37      fileContent: fileContent,
38      fileRoot: file,
39    });
40  });
41  fileContentList.forEach(item => {
42    const fileName = item.fileName.replace(/\.d.ts$/g, '.ts');
43    let packageName = item.fileRoot.indexOf('component\\ets\\') >= 0 ||
44      item.fileRoot.indexOf('component/ets/') >= 0 ? 'ArkUI' : fileName.replace((/\@|.ts$/g), '').replace(/D:\\/g, '');
45    ts.transpileModule(item.fileContent, {
46      compilerOptions: {
47        'target': ts.ScriptTarget.ES2017,
48      },
49      fileName: fileName,
50      transformers: { before: [getReturnDeclarationArr(packageName, exportApi, returnDeclarationArr)] },
51    });
52  });
53
54  fileContentList.forEach(item => {
55    const fileName = item.fileName.replace(/\.d.ts$/g, '.ts');
56    let packageName = item.fileRoot.indexOf('component\\ets\\') >= 0 ||
57      item.fileRoot.indexOf('component/ets/') >= 0 ? 'ArkUI' : fileName.replace(/\@|.ts$/g, '').replace(/D:\\/g, '');
58    ts.transpileModule(item.fileContent, {
59      compilerOptions: {
60        'target': ts.ScriptTarget.ES2017,
61      },
62      fileName: fileName,
63      transformers: { before: [processDeclarationSourceFile(packageName, api, exportApi, returnDeclarationArr, hash, item.fileRoot)] },
64    });
65  });
66  return api;
67}
68
69// 获取返回值类型
70function visitAllNode(node, returnDeclarationArr) {
71  if ((ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) && node && node.type &&
72    ts.isTypeReferenceNode(node.type)) {
73    returnDeclarationArr.add(node.type.typeName.getText());
74  }
75  node.getChildren().forEach(item => {
76    visitAllNode(item, returnDeclarationArr);
77  });
78}
79
80// 获取导入Api的数组
81function getExportApi(node, packageName, exportApi) {
82  node.statements.forEach(stat => {
83    if (ts.isModuleDeclaration(stat)) {
84      if (stat.getText().indexOf('namespace') > 0) {
85        let apiInfo = {
86          isSystemApi: '公开API',
87          version: '',
88          deprecated: '',
89          permission: 'N/A',
90          sysCap: 'N/A',
91          model: '',
92        };
93        exportApi.push({
94          packageName: packageName,
95          className: stat.name.escapedText.toString(),
96          methodName: '',
97          apiInfo: getApiInfo(stat, apiInfo),
98        });
99      }
100    }
101  });
102}
103
104// 获取返回值类型和命名空间
105const getReturnDeclarationArr = (packageName, exportApi, returnDeclarationArr) => {
106  return (context) => {
107    return (node) => {
108      visitAllNode(node, returnDeclarationArr);
109      getExportApi(node, packageName, exportApi);
110      return node;
111    };
112  };
113};
114
115
116// 搜集API接口并去重
117function processDeclarationSourceFile(packageName, api, exportApi, returnDeclarationArr, hash, dtsPath) {
118  return (context) => {
119    return (node) => {
120      const statements = node.statements;
121      const currentClassFuncSet = new Set([]);
122      const currentTypeList = new Array();
123      getCurrentTypeList(statements, currentTypeList);
124
125      statements.forEach(stat => {
126        let apiInfo = {
127          isSystemApi: '公开API',
128          version: '',
129          deprecated: '',
130          permission: 'N/A',
131          sysCap: 'N/A',
132          model: '',
133          headimport: 'N/A',
134          endexport: 'N/A',
135        };
136        collectApi(packageName, api, stat, apiInfo, exportApi, returnDeclarationArr, hash, dtsPath,
137          currentTypeList, currentClassFuncSet);
138      });
139      return node;
140    };
141  };
142}
143
144function getCurrentTypeList(statements, currentTypeList) {
145  statements.forEach(stat => {
146    if (ts.isTypeAliasDeclaration(stat)) {
147      if (stat.type.types) {
148        let typeObj = {
149          name: stat.name.escapedText,
150          value: [],
151        };
152        stat.type.types.forEach(type => {
153          if (type.literal && type.literal.text) {
154            typeObj.value.push(type.literal.text);
155          }
156        });
157        if (typeObj.value.length > 0) {
158          currentTypeList.push(typeObj);
159        }
160      }
161    }
162  });
163}
164
165function collectApi(packageName, api, stat, apiInfo, exportApi, returnDeclarationArr, hash, dtsPath, currentTypeList,
166  currentClassFuncSet) {
167  if (ts.isInterfaceDeclaration(stat)) {
168    collectInterfaceDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo,
169      currentTypeList, dtsPath);
170  } else if (ts.isModuleDeclaration(stat)) {
171    collectModuleDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo,
172      currentTypeList, dtsPath);
173  } else if (ts.isClassDeclaration(stat)) {
174    collectClassDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo,
175      currentTypeList, dtsPath);
176  } else if (ts.isEnumDeclaration(stat)) {
177    collectEnumDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo,
178      currentTypeList, dtsPath);
179  } else if (ts.isVariableStatement(stat)) {
180    const apiName = stat.declarationList.declarations[0].name.escapedText;
181    addApi(packageName, 'global', apiName, stat.getText().trim(), getApiInfo(stat, apiInfo),
182      'Decorator', api, hash, dtsPath, 8);
183  } else {
184    collectSpecialApi(stat, packageName, api, hash, currentClassFuncSet, dtsPath, exportApi, apiInfo);
185  }
186}
187
188function collectSpecialApi(stat, packageName, api, hash, currentClassFuncSet, dtsPath, exportApi, apiInfo) {
189  if (ts.isMethodDeclaration(stat) || ts.isMethodSignature(stat) || ts.isFunctionDeclaration(stat)) {
190    const methodName = stat.name.escapedText ? stat.name.escapedText.toString() : stat.name.text.toString();
191    let className = '';
192    exportApi.forEach(item => {
193      if (item.methodName === methodName && item.packageName === packageName) {
194        className = item.className;
195        if (item.apiInfo) {
196          apiInfo = item.apiInfo;
197        }
198      }
199    });
200    addFunctionOnOffApi(packageName, className, methodName, getApiInfo(stat, apiInfo), 'Method', api,
201      hash, currentClassFuncSet, stat, dtsPath);
202  }
203}
204
205function collectInterfaceDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo, currentTypeList, dtsPath) {
206  const className = stat.name.escapedText.toString();
207  const interfaceChildren = stat.members;
208  let tmpApiInfo = getApiInfo(stat, apiInfo);
209  collectEachChildNode(interfaceChildren, packageName, className, 'Field', api, exportApi, returnDeclarationArr, hash,
210    tmpApiInfo, currentTypeList, dtsPath);
211}
212
213function collectClassDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo, currentTypeList, dtsPath) {
214  const className = stat.name.escapedText.toString();
215  const classChildren = stat.members;
216  let tmpApiInfo = getApiInfo(stat, apiInfo);
217  collectEachChildNode(classChildren, packageName, className, 'Field', api, exportApi, returnDeclarationArr, hash,
218    tmpApiInfo, currentTypeList, dtsPath);
219}
220
221function collectEnumDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo, currentTypeList, dtsPath) {
222  const className = stat.name.escapedText.toString();
223  const enumChildren = stat.members;
224  let tmpApiInfo = getApiInfo(stat, apiInfo);
225  collectEachChildNode(enumChildren, packageName, className, 'Enum', api, exportApi, returnDeclarationArr, hash,
226    tmpApiInfo, currentTypeList, dtsPath);
227}
228
229function collectModuleDeclaration(stat, packageName, api, exportApi, returnDeclarationArr, hash, apiInfo, currentTypeList, dtsPath) {
230  const className = stat.name.escapedText ? stat.name.escapedText.toString() : stat.name.text.toString();
231  const moduleChildren = stat.body.statements;
232  let tmpApiInfo = getApiInfo(stat, apiInfo);
233  collectEachChildNode(moduleChildren, packageName, className, 'Field', api, exportApi, returnDeclarationArr, hash,
234    tmpApiInfo, currentTypeList, dtsPath);
235}
236
237function collectTypeApi(child, packageName, className, api, hash, apiInfo, dtsPath) {
238  let typeObj = {
239    name: child.name.escapedText,
240    value: [],
241  };
242  if (child.type.types) {
243    child.type.types?.forEach(type => {
244      collectTypeApiInTypes(type, apiInfo, child, api, hash, dtsPath, typeObj, packageName);
245    });
246  } else if (child.type.members) {
247    child.type.members?.forEach(member => {
248      collectTypeApiInMembers(member, apiInfo, child, api, hash, dtsPath, typeObj, packageName, className);
249    });
250  }
251  return typeObj;
252}
253
254function collectTypeApiInTypes(type, apiInfo, child, api, hash, dtsPath, typeObj, packageName) {
255  if (type.literal && type.literal.text) {
256    typeObj.value.push(type.literal.text);
257    if (typeCollection && type.literal) {
258      let faterApiInfo = JSON.parse(JSON.stringify(apiInfo));
259      addApi(packageName, child.name.escapedText, type.literal.text, child.getText(),
260        getApiInfo(child, faterApiInfo), 'Type', api, hash, dtsPath, 1);
261    }
262  } else {
263    if (type.getText() !== '') {
264      typeObj.value.push(type.getText());
265      if (typeCollection && type.literal) {
266        let faterApiInfo = JSON.parse(JSON.stringify(apiInfo));
267        addApi(packageName, child.name.escapedText, type.getText(), child.getText(),
268          getApiInfo(child, faterApiInfo), 'Type', api, hash, dtsPath, 2);
269      }
270    }
271  }
272}
273
274function collectTypeApiInMembers(member, apiInfo, child, api, hash, dtsPath, typeObj, packageName, className) {
275  member.type.types?.forEach(type => {
276    if (type.literal && type.literal.text) {
277      typeObj.value.push(type.literal.text);
278      if (typeCollection) {
279        let faterApiInfo = JSON.parse(JSON.stringify(apiInfo));
280        addApi(packageName, child.name.escapedText, type.literal.text, child.getText(),
281          getApiInfo(child, faterApiInfo), 'Type', api, hash, dtsPath, 3);
282      }
283    } else {
284      if (type.getText() !== '') {
285        typeObj.value.push(type.getText());
286        if (typeCollection) {
287          let faterApiInfo = JSON.parse(JSON.stringify(apiInfo));
288          addApi(packageName, className, child.name.escapedText, child.getText(),
289            getApiInfo(child, faterApiInfo), 'Type', api, hash, dtsPath, 4);
290        }
291      }
292    }
293  });
294}
295
296function collectEachChildNode(children, packageName, className, faterApiType, api, exportApi, returnDeclarationArr,
297  hash, apiInfo, currentTypeList, dtsPath) {
298  const currentClassFunSet = new Set([]);
299  children.forEach(child => {
300    if (ts.isTypeAliasDeclaration(child)) {
301      if (child.type) {
302        let typeObj = collectTypeApi(child, packageName, className, api, hash, apiInfo, dtsPath);
303        if (typeObj.value.length > 0) {
304          currentTypeList.push(typeObj);
305        }
306      }
307    }
308  });
309  children.forEach(child => {
310    let faterApiInfo = JSON.parse(JSON.stringify(apiInfo));
311    let apiType = new String(faterApiType);
312    if (/export.*\{.*\}/g.test(child.getText())) {
313      exportApi.push({
314        packageName: packageName,
315        className: className,
316        methodName: child.getText().replace('export', '').replace('{', '').replace('}', '').replace(';', '').trim(),
317        apiInfo: faterApiInfo,
318      });
319      return;
320    }
321    if (ts.isInterfaceDeclaration(child)) {
322      collectInterfaceDeclaration(child, packageName, api, exportApi, returnDeclarationArr, hash, faterApiInfo, currentTypeList, dtsPath);
323    } else if (ts.isModuleDeclaration(child)) {
324      collectModuleDeclaration(child, packageName, api, exportApi, returnDeclarationArr, hash, faterApiInfo, currentTypeList, dtsPath);
325    } else if (ts.isClassDeclaration(child)) {
326      collectClassDeclaration(child, packageName, api, exportApi, returnDeclarationArr, hash, faterApiInfo, currentTypeList, dtsPath);
327    } else if (ts.isEnumDeclaration(child)) {
328      collectEnumDeclaration(child, packageName, api, exportApi, returnDeclarationArr, hash, faterApiInfo, currentTypeList, dtsPath);
329    } else {
330      if ((ts.isMethodDeclaration(child) || ts.isMethodSignature(child) || ts.isFunctionDeclaration(child)) &&
331        (child.name.escapedText === 'on' || child.name.escapedText === 'off') && child.parameters && child.parameters.length > 0) {
332        apiType = 'Method';
333        collectSubscriptionTypeApi(child, apiType, packageName, className, faterApiType, api, currentTypeList,
334          hash, currentClassFunSet, dtsPath);
335      } else {
336        collectOtherApi(child, packageName, className, faterApiInfo, apiType, api,
337          hash, currentClassFunSet, child, dtsPath, returnDeclarationArr);
338      }
339    }
340  });
341}
342
343function collectOtherApi(child, packageName, className, faterApiInfo, apiType, api,
344  hash, currentClassFunSet, child, dtsPath, returnDeclarationArr) {
345  let methodName = '';
346  if (isSpecialMethod(child)) {
347    if (child.name) {
348      methodName = child.name.getText();
349    } else {
350      methodName = className;
351    }
352    apiType = 'Method';
353  } else if (ts.isPropertyDeclaration(child) || ts.isPropertySignature(child)) {
354    if (child.type && child.type.parameters) {
355      methodName = child.name.escapedText;
356      apiType = 'Method';
357    } else {
358      methodName = child.name.escapedText;
359      apiType = 'Field';
360    }
361  } else {
362    if (child.name) {
363      methodName = child.name.getText();
364    }
365  }
366  if (methodName !== '') {
367    addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
368      hash, currentClassFunSet, child, dtsPath);
369  } else {
370    if (child.getText().indexOf('constructor') === 0) {
371      methodName = 'constructor';
372      apiType = 'Method';
373    } else if (child.getText().indexOf('const') === 0) {
374      const infoObj = collectFieleOrConstant(apiType, methodName, child, returnDeclarationArr);
375      methodName = infoObj.methodName;
376      apiType = infoObj.apiType;
377    } else if (/\w+:\s*\w+/g.test(child.getText())) {
378      apiType = 'Field';
379      methodName = child.getText().split(':')[0].trim();
380    }
381    if (methodName !== '') {
382      addApi(packageName, className, methodName, child.getText(),
383        getApiInfo(child, faterApiInfo), apiType, api, hash, dtsPath, 5);
384    }
385  }
386}
387
388function isSpecialMethod(child){
389  return ts.isMethodDeclaration(child) || ts.isMethodSignature(child) || ts.isFunctionDeclaration(child) ||
390  ts.isCallSignatureDeclaration(child) || ts.isConstructSignatureDeclaration(child) ||
391  ts.isIndexSignatureDeclaration(child);
392}
393
394function collectFieleOrConstant(apiType, methodName, child, returnDeclarationArr) {
395  if (child.getText().replace('const', '').indexOf(':') > 0) {
396    if (returnDeclarationArr.has(child.getText().replace('const', '').split(':')[1].trim())) {
397      apiType = 'Field';
398    } else {
399      apiType = 'Constant';
400    }
401    methodName = child.getText().replace('const', '').split(':')[0].trim();
402  } else if (child.getText().replace('const', '').indexOf('=') > 0) {
403    if (returnDeclarationArr.has(child.getText().replace('const', '').split('=')[1].trim())) {
404      apiType = 'Field';
405    } else {
406      apiType = 'Constant';
407    }
408    methodName = child.getText().replace('const', '').split('=')[0].trim();
409  }
410  return { apiType, methodName };
411}
412
413function collectSubscriptionTypeApi(child, apiType, packageName, className, faterApiInfo, api, currentTypeList,
414  hash, currentClassFunSet, dtsPath) {
415  for (let i = 0; i < child.parameters.length; i++) {
416    const param = child.parameters[i];
417    if (isCommonSubscriptionType(param)) {
418      if (param.type && param.type.literal && param.type.literal.text) {
419        collectTypeOrEventApi(packageName, className, faterApiInfo, apiType, api,
420          hash, currentClassFunSet, child, dtsPath, param);
421      } else if (param.type && param.type.types && param.type.types.length > 0) {
422        collectSpecialSubscriptionTypeApi(param, packageName, className, faterApiInfo, apiType, api,
423          hash, currentClassFunSet, child, dtsPath);
424      } else if (param.type && param.type.typeName && param.type.typeName.escapedText) {
425        inCurrentTypeListApi(packageName, className, faterApiInfo, apiType, api,
426          hash, currentClassFunSet, child, dtsPath, currentTypeList, param);
427      } else if (param.type && param.type.typeName && param.type.typeName.left &&
428        param.type.typeName.right) {
429        let methodName = child.name.escapedText + '_' + param.type.typeName.left.escapedText + '_' +
430          param.type.typeName.right.escapedText;
431        addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
432          hash, currentClassFunSet, child, dtsPath);
433      } else {
434        let methodName = child.name.escapedText;
435        addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
436          hash, currentClassFunSet, child, dtsPath);
437      }
438      break;
439    } else {
440      let methodName = child.name.escapedText;
441      addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
442        hash, currentClassFunSet, child, dtsPath);
443    }
444  }
445}
446
447function isCommonSubscriptionType(param) {
448  return param.name.escapedText === 'type' || param.name.escapedText === 'event' ||
449    param.name.escapedText === 'eventType';
450}
451
452function collectSpecialSubscriptionTypeApi(param, packageName, className, faterApiInfo, apiType, api,
453  hash, currentClassFunSet, child, dtsPath) {
454  param.type.types.forEach(type => {
455    if (type.literal && type.literal.text) {
456      const methodName = child.name.escapedText + '_' + type.literal.text;
457      addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
458        hash, currentClassFunSet, child, dtsPath);
459    }
460  });
461}
462
463function inCurrentTypeListApi(packageName, className, faterApiInfo, apiType, api, hash, currentClassFunSet, child,
464  dtsPath, currentTypeList, param) {
465  if (currentTypeList && currentTypeList.length > 0) {
466    currentTypeList.forEach(type => {
467      if (type.name === param.type.typeName.escapedText) {
468        type.value.forEach(typeString => {
469          let methodName = child.name.escapedText + '_' + typeString;
470          addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
471            hash, currentClassFunSet, child, dtsPath);
472        });
473      }
474    });
475  } else {
476    let methodName = child.name.escapedText;
477    addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
478      hash, currentClassFunSet, child, dtsPath);
479  }
480}
481
482function collectTypeOrEventApi(packageName, className, faterApiInfo, apiType, api,
483  hash, currentClassFunSet, child, dtsPath, param) {
484  const typeTextArr = param.getText().replace(/\s*/g, '').split(':');
485  if (typeTextArr[0] === 'type' || typeTextArr[0] === 'event') {
486    let methodName = child.name.escapedText + '_' + param.type.literal.text;
487    addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
488      hash, currentClassFunSet, child, dtsPath);
489  } else {
490    let methodName = child.name.escapedText + '_' + param.type.literal.text;
491    addFunctionOnOffApi(packageName, className, methodName, faterApiInfo, apiType, api,
492      hash, currentClassFunSet, child, dtsPath);
493  }
494}
495
496function addFunctionOnOffApi(packageName, className, methodName, apiInfo, apiType, api,
497  hash, currentClassFunSet, childNode, dtsPath) {
498  // 合并同名函数
499  if (currentClassFunSet.has(methodName) && !isNotMerge) {
500    collectSameNameApiText(api, packageName, className, methodName, childNode);
501  } else {
502    notMergeSameNameFun(packageName, className, methodName, apiInfo, apiType, api,
503      hash, currentClassFunSet, childNode, dtsPath);
504  }
505}
506
507function collectSameNameApiText(api, packageName, className, methodName, childNode) {
508  for (let i = 0; i < api.length; i++) {
509    const curApi = api[i];
510    if (curApi.packageName === packageName && curApi.className === className &&
511      curApi.methodName === methodName) {
512      if (curApi.methodText.indexOf(`${childNode.getText().replace('declare', '').trim()}`) < 0) {
513        curApi.methodText += `\n${childNode.getText().replace('declare', '').replace(/\r|\n/ig, '').trim()}`;
514        break;
515      }
516    }
517  }
518}
519
520function notMergeSameNameFun(packageName, className, methodName, apiInfo, apiType, api,
521  hash, currentClassFunSet, childNode, dtsPath) {
522  if (!currentClassFunSet.has(methodName)) {
523    currentClassFunSet.add(methodName);
524    addApi(packageName, className, methodName, childNode.getText().replace('declare', '').trim(),
525      getApiInfo(childNode, apiInfo), apiType, api, hash, dtsPath, 6);
526  } else {
527    if (childNode.getFullText().indexOf('\/**') >= 0) {
528      addApi(packageName, className, methodName, childNode.getText().replace('declare', '').trim(),
529        getApiInfo(childNode, apiInfo), apiType, api, hash, dtsPath, 7);
530    } else {
531      let firstApiInfo = {};
532      handleSameNameApiJsDoc(api, firstApiInfo, packageName, className, methodName);
533      addApi(packageName, className, methodName, childNode.getText().replace('declare', '').trim(),
534        firstApiInfo, apiType, api, hash, dtsPath, 8);
535    }
536  }
537}
538
539function handleSameNameApiJsDoc(api, firstApiInfo, packageName, className, methodName) {
540  for (let i = 0; i < api.length; i++) {
541    const curApi = api[i];
542    if (curApi.packageName === packageName && curApi.className === className &&
543      curApi.methodName === methodName) {
544      firstApiInfo.isSystemApi = curApi.isSystemApi;
545      firstApiInfo.version = curApi.version;
546      firstApiInfo.sysCap = curApi.sysCap;
547      firstApiInfo.permission = curApi.permission;
548      firstApiInfo.model = curApi.model;
549      firstApiInfo.deprecated = curApi.deprecated;
550    }
551  }
552}
553
554function getApiInfo(node, apiInfo) {
555  const notesStr = node.getFullText().replace(node.getText(), '');
556  apiInfo.model = getModelInfo(notesStr);
557  apiInfo.errorCode = getErrorCode(notesStr);
558  apiInfo.deprecated = getDeprecatedInfo(notesStr);
559  apiInfo.permission = getPermissionInfo(notesStr);
560  apiInfo.isSystemApi = getSystemApi(notesStr);
561  apiInfo.version = getSinceVersion(notesStr);
562  apiInfo.sysCap = getSyscap(notesStr);
563  apiInfo.formInfo = isForm(notesStr);
564  apiInfo.isCrossPlatform = isCrossPlatform(notesStr);
565  return apiInfo;
566}
567
568function getSystemApi(notesStr) {
569  let isSystemApi = '';
570  if (/\@[S|s][Y|y][S|s][T|t][E|e][M|m][A|a][P|p][I|i]/g.test(notesStr)) {
571    isSystemApi = '系统API';
572  } else {
573    isSystemApi = '公开API';
574  }
575}
576
577function getSinceVersion(notesStr) {
578  let version;
579  if (/\@[S|s][I|i][N|n][C|c][E|e]\s*(\d+)/g.test(notesStr)) {
580    notesStr.replace(/\@[S|s][I|i][N|n][C|c][E|e]\s*(\d+)/g, (versionInfo) => {
581      version = versionInfo.replace(/\@[S|s][I|i][N|n][C|c][E|e]/g, '').trim();
582    });
583  } else {
584    version = 'N/A';
585  }
586  return version;
587}
588
589function getSyscap(notesStr) {
590  let syscap = '';
591  if (/\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) {
592    notesStr.replace(/\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g, sysCapInfo => {
593      syscap = sysCapInfo.replace(/\@[S|s][Y|y][S|s][C|c][A|a][P|p]/g, '').trim();
594    });
595  }
596  return syscap;
597}
598
599function isForm(notesStr) {
600  let formInfo = '';
601  if (/\@form/g.test(notesStr)) {
602    formInfo = '是';
603  } else {
604    formInfo = '否';
605  }
606  return formInfo;
607}
608
609function getPermissionInfo(notesStr) {
610  let permission = '';
611  if (/\@[P|p][E|e][R|r][M|m][I|i][S|s][S|s][I|i][O|o][N|n]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) {
612    notesStr.replace(/\@[P|p][E|e][R|r][M|m][I|i][S|s][S|s][I|i][O|o][N|n]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g,
613      permissionInfo => {
614        permission = permissionInfo.replace(
615          /\@[P|p][E|e][R|r][M|m][I|i][S|s][S|s][I|i][O|o][N|n]/g, '').trim();
616        return permission;
617      });
618  } else {
619    permission = 'N/A';
620  }
621  return permission;
622}
623
624function getDeprecatedInfo(notesStr) {
625  let deprecated = '';
626  if (/\@[D|d][E|e][P|p][R|r][E|e][C|c][A|a][T|t][E|e][D|d].*[S|s][I|i][N|n][C|c][E|e]\s*(\d+)/g.test(notesStr)) {
627    notesStr.replace(/\@[D|d][E|e][P|p][R|r][E|e][C|c][A|a][T|t][E|e][D|d].*[S|s][I|i][N|n][C|c][E|e]\s*(\d+)/g,
628      deprecatedInfo => {
629        deprecated = deprecatedInfo.replace(
630          /\@[D|d][E|e][P|p][R|r][E|e][C|c][A|a][T|t][E|e][D|d].*[S|s][I|i][N|n][C|c][E|e]\s*/g, '').trim();
631
632      });
633  } else {
634    deprecated = 'N/A';
635  }
636  return deprecated;
637}
638
639function getErrorCode(notesStr) {
640  let errorCode = '';
641  if (/\@throws { BusinessError } \d{2,18}/g.test(notesStr)) {
642    notesStr.replace(/\@throws { BusinessError } \d{2,18}/g, (code) => {
643      if (errorCode === '') {
644        errorCode += `${code.replace(/\@throws { BusinessError } /, '')}`;
645      } else {
646        errorCode += `,${code.replace(/\@throws { BusinessError } /, '')}`;
647      }
648    });
649  } else {
650    errorCode = 'N/A';
651  }
652  return errorCode;
653}
654
655function getModelInfo(notesStr) {
656  let model = '';
657  if (/\@[F|f][A|a][M|m][O|o][D|d][E|e][L|l][O|o][N|n][L|l][Y|y]/g.test(notesStr)) {
658    notesStr.replace(/\@[F|f][A|a][M|m][O|o][D|d][E|e][L|l][O|o][N|n][L|l][Y|y]/g, modelInfo => {
659      model = modelInfo;
660    });
661  } else if (/\@[S|s][T|t][A|a][G|g][E|e][M|m][O|o][D|d][E|e][L|l][O|o][N|n][L|l][Y|y]/g.test(notesStr)) {
662    notesStr.replace(/\@[S|s][T|t][A|a][G|g][E|e][M|m][O|o][D|d][E|e][L|l][O|o][N|n][L|l][Y|y]/g, modelInfo => {
663      model = modelInfo;
664    });
665  } else {
666    model = 'N/A';
667  }
668  return model;
669}
670
671function isCrossPlatform(notesStr) {
672  let isCrossPlatform = '';
673  if (/\@crossplatform/g.test(notesStr)) {
674    isCrossPlatform = '是';
675  } else {
676    isCrossPlatform = '否';
677  }
678  return isCrossPlatform;
679}
680
681function addApi(packageName, className, methodName, methodText, apiInfo, apiType, api, hash, dtsPath, type) {
682  let recard = isNotMerge ? `${packageName}.${className}/${methodName}/${methodText}` :
683    `${packageName}.${className}/${methodName}`;
684  if (!hash.has(recard)) {
685    hash.add(recard);
686    api.push({
687      packageName: packageName,
688      className: className,
689      methodName: methodName,
690      methodText: methodText.replace(/export\s/g, ''),
691      isSystemApi: apiInfo.isSystemApi,
692      version: apiInfo.version,
693      deprecated: apiInfo.deprecated,
694      apiType: apiType,
695      sysCap: apiInfo.sysCap,
696      permission: apiInfo.permission,
697      model: apiInfo.model,
698      dtsPath: dtsPath,
699      errorCode: apiInfo.errorCode,
700      formInfo: apiInfo.formInfo,
701      isCrossPlatform: apiInfo.isCrossPlatform,
702    });
703  }
704}
705
706function readFile(dir, utFiles) {
707  try {
708    const files = fs.readdirSync(dir);
709    files.forEach((element) => {
710      const filePath = path.join(dir, element);
711      const status = fs.statSync(filePath);
712      if (status.isDirectory()) {
713        readFile(filePath, utFiles);
714      } else {
715        if (/\.d\.ts/.test(filePath)) {
716          utFiles.push(filePath);
717        }
718      }
719    });
720  } catch (e) {
721    console.error('ETS ERROR: ' + e);
722  }
723}
724
725function exportData() {
726  const dir = path.resolve(__dirname, './API');
727  let fileList = [];
728  readFile(dir, fileList);
729  let api = parse(fileList);
730  excel(api);
731}
732
733async function excel(api) {
734  let buffer = await getExcelBuffer(api);
735  fs.writeFile('Js_Api.xlsx', buffer, function (err) {
736    if (err) {
737      console.error(err);
738      return;
739    }
740  });
741}
742
743async function getExcelBuffer(api) {
744  const workbook = new ExcelJS.Workbook();
745  const sheet = workbook.addWorksheet('Js Api', { views: [{ xSplit: 1 }] });
746  sheet.getRow(1).values = ['模块名', '类名', '方法名', '函数', '类型', 'SysCap',
747    '权限', '支持起始版本', '访问级别', '是否为卡片', '是否支持跨平台'];
748  for (let i = 1; i <= api.length; i++) {
749    const apiData = api[i - 1];
750    sheet.getRow(i + 1).values = [apiData.packageName, apiData.className, apiData.methodName,
751      apiData.methodText, apiData.apiType, apiData.sysCap, apiData.permission,
752      apiData.version, apiData.isSystemApi, apiData.formInfo, apiData.isCrossPlatform];
753  }
754  const buffer = await workbook.xlsx.writeBuffer();
755  return buffer;
756}
757
758exportData();