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 { ReporterFormat } = require('./configs');
17const { Logger } = require('./utils');
18const exceljs = require('exceljs');
19const fs = require('fs');
20const path = require('path');
21
22class ApiJsonWriter {
23  constructor(outputPath) {
24    this.outputPath = outputPath;
25    this.apiInfos = [];
26    this.noSerializeKeys = new Set(['apiSourceFile', 'apiNode']);
27  }
28
29  add(apiInfos) {
30    this.apiInfos.push(...apiInfos);
31  }
32
33  flush() {
34    const output = path.resolve(this.outputPath, 'collectedApi.json');
35    fs.writeFileSync(output, JSON.stringify(this.apiInfos, (key, value) => {
36      if (this.noSerializeKeys.has(key)) {
37        return undefined;
38      }
39      return value;
40    }));
41    Logger.info('ApiJsonWriter', `report is in ${output}`);
42  }
43}
44
45class ApiExcelWriter {
46  constructor(outputDir, noRepeat) {
47    this.outputDir = outputDir;
48    this.apiInfos = [];
49    this.enable = true;
50    this.noRepeat = noRepeat;
51  }
52
53  close() {
54    this.enable = false;
55  }
56
57  open() {
58    this.enable = true;
59  }
60
61  add(apiInfos) {
62    this.apiInfos.push(...apiInfos.filter((value) => {
63      return !(value.packageName === 'ArkUI' && value.qualifiedTypeName === '');
64    }));
65  }
66
67  async flush() {
68    if (!this.enable) {
69      return;
70    }
71    this.writeSubscribeApi();
72    this.writeAppApi();
73  }
74
75  async writeSubscribeApi() {
76    const apiInfoSet = new Set();
77    const subscribeWorkbook = new exceljs.Workbook();
78    const subscribeSheet = subscribeWorkbook.addWorksheet('Js Api', { views: [{ xSplit: 1 }] });
79    subscribeSheet.getRow(1).values = ['类名', '接口名', '接口类型', '方法声明', '接口全路径'];
80    let lineNumber = 0;
81    const STARTING_LINE_NUMBER = 2;
82    this.apiInfos.forEach((apiInfo, index) => {
83      const typeName = apiInfo.qualifiedTypeName ? apiInfo.qualifiedTypeName :
84        (apiInfo.typeName ? apiInfo.typeName : 'unnamed');
85
86      if (!apiInfoSet.has(formatInfo(apiInfo, typeName))) {
87        subscribeSheet.getRow(lineNumber + STARTING_LINE_NUMBER).values = [
88          typeName,
89          apiInfo.propertyName,
90          apiInfo.apiType,
91          apiInfo.apiText.replace(/\;$/g, ''),
92          apiInfo.dtsPath
93        ];
94        lineNumber++;
95        apiInfoSet.add(formatInfo(apiInfo, typeName));
96      }
97    });
98    const subscribeBuffer = await subscribeWorkbook.xlsx.writeBuffer();
99    const subscribeOutputFile = path.resolve(this.outputDir, 'subscribe_api.xlsx');
100    fs.writeFileSync(subscribeOutputFile, subscribeBuffer);
101  }
102
103  async writeAppApi() {
104    let lineNumber = 0;
105    const apiInfoSet = new Set();
106    const workbook = new exceljs.Workbook();
107    const sheet = workbook.addWorksheet('Js Api', { views: [{ xSplit: 1 }] });
108    sheet.getRow(1).values = ['模块名', '类名', '方法名', '函数', '文件位置'];
109    this.apiInfos.forEach((apiInfo, index) => {
110      const typeName = apiInfo.componentName ? apiInfo.componentName :
111        (apiInfo.typeName ? apiInfo.typeName : apiInfo.qualifiedTypeName);
112      if (this.noRepeat && !apiInfoSet.has(formatInfo(apiInfo, typeName))) {
113        this.createSheet(sheet, typeName, apiInfo, lineNumber);
114        apiInfoSet.add(formatInfo(apiInfo, typeName));
115        lineNumber++;
116      } else if (!this.noRepeat) {
117        this.createSheet(sheet, typeName, apiInfo, index);
118      }
119    });
120    const buffer = await workbook.xlsx.writeBuffer();
121    const outputFile = path.resolve(this.outputDir, 'app_api.xlsx');
122    fs.writeFileSync(outputFile, buffer);
123    Logger.info('ApiExcelWriter', `report is in ${outputFile}`);
124  }
125
126  createSheet(sheet, typeName, apiInfo, lineNumber) {
127    const STARTING_LINE_NUMBER = 2;
128    sheet.getRow(lineNumber + STARTING_LINE_NUMBER).values = [
129      path.basename(apiInfo.packageName, '.d.ts').replace('@', ''),
130      typeName,
131      apiInfo.propertyName,
132      apiInfo.apiRawText,
133      `${apiInfo.sourceFileName}(${apiInfo.pos})`
134    ];
135  }
136}
137
138class ApiWriter {
139  constructor(outputPath, formatFlag, noRepeat) {
140    this.outputPath = outputPath;
141    this.formatFlag = formatFlag;
142    this.noRepeat = noRepeat;
143    this.apiInfos = [];
144  }
145
146  add(apiInfos) {
147    this.apiInfos.push(...apiInfos);
148  }
149
150  async flush() {
151    if (this.formatFlag === ReporterFormat.FLAG_JSON) {
152      this.writeJson(this.apiInfos);
153    } else if (this.formatFlag === ReporterFormat.FLAG_EXCEL) {
154      await this.writeExcel(this.apiInfos);
155    } else if (this.formatFlag === ReporterFormat.FLAG_DEBUG) {
156      this.writeJson(this.apiInfos);
157      await this.writeExcel(this.apiInfos);
158    } else {
159      this.writeJson(this.apiInfos);
160    }
161  }
162
163  writeJson(apiInfos) {
164    const apiJsonWriter = new ApiJsonWriter(this.outputPath);
165    apiJsonWriter.add(apiInfos);
166    apiJsonWriter.flush();
167  }
168
169  async writeExcel(apiInfos) {
170    const apiExcelWriter = new ApiExcelWriter(this.outputPath, this.noRepeat);
171    apiExcelWriter.add(apiInfos);
172    await apiExcelWriter.flush();
173  }
174}
175
176function formatInfo(apiInfo, typeName) {
177  return `${typeName}_${apiInfo.propertyName}_${apiInfo.apiText}_ ${apiInfo.dtsPath}`;
178}
179
180exports.ApiJsonWriter = ApiJsonWriter;
181exports.ApiExcelWriter = ApiExcelWriter;
182exports.ApiWriter = ApiWriter;