• 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 fs from 'fs';
17import path from 'path';
18import ts from 'typescript';
19import _ from 'lodash';
20
21import { NodeProcessorHelper, parserParam } from './NodeProcessor';
22import { ResultsProcessHelper } from './ResultsProcess';
23import { ApiType, BasicApiInfo, ApiInfo } from '../../typedef/parser/ApiInfoDefination';
24import { StringConstant } from '../../utils/Constant';
25import { FileUtils } from '../../utils/FileUtils';
26import { DiffHelper } from '../diff/diff';
27import * as ResultsInfo from '../../typedef/parser/ResultsInfo';
28
29/**
30 * 解析d.ts工具类
31 */
32export class Parser {
33  static needLib: boolean = false;
34  static cleanParserParamSDK(): void {
35    parserParam.setFileDir('');
36    parserParam.setSdkPath('');
37  }
38  /**
39   * 根据传入的文件目录,对目录下的d.ts文件进行解析,
40   * 并将其整合到一个map对象中
41   *
42   * @param { string } fileDir 传入的文件目录
43   * @param { string } [collectFile = ''] 统计的文件或目录
44   * @returns { FilesMap } 返回解析后的map对象
45   */
46  static parseDir(fileDir: string, collectFile: string = ''): FilesMap {
47    Parser.cleanParserParamSDK();
48    const files: Array<string> = FileUtils.readFilesInDir(fileDir, (name) => {
49      return name.endsWith(StringConstant.DTS_EXTENSION) || name.endsWith(StringConstant.DETS_EXTENSION);
50    });
51    const apiMap: FilesMap = new Map();
52    let collectFiles: Array<string> = [];
53    if (collectFile === '') {
54      parserParam.setSdkPath(fileDir);
55      collectFiles = files;
56    } else if (FileUtils.isDirectory(collectFile)) {
57      Parser.needLib = true;
58      parserParam.setSdkPath(collectFile);
59      collectFiles = FileUtils.readFilesInDir(collectFile, (name) => {
60        return name.endsWith(StringConstant.DTS_EXTENSION) || name.endsWith(StringConstant.DETS_EXTENSION);
61      });
62    } else if (FileUtils.isFile(collectFile)) {
63      Parser.needLib = true;
64      parserParam.setSdkPath(path.resolve(collectFile, '..'));
65      collectFiles = [collectFile];
66    }
67    parserParam.setFileDir(fileDir);
68    parserParam.setRootNames(collectFiles);
69    if (Parser.needLib) {
70      parserParam.setLibPath(path.resolve(FileUtils.getBaseDirName(), './libs'));
71    }
72    parserParam.setProgram();
73    collectFiles.forEach((filePath: string) => {
74      Parser.parseFile(fileDir, filePath, apiMap);
75    });
76    return apiMap;
77  }
78
79  /**
80   * 根据文件名解析api信息
81   *
82   * @param { string } fileDir 文件的跟路径
83   * @param { string } filePath 文件路径名
84   * @param { FilesMap } [apiMap] 返回处理后的存储api信息的map对象
85   * @returns { FilesMap } 返回处理后的存储api信息的map对象
86   */
87  static parseFile(fileDir: string, filePath: string, apiMap?: FilesMap): FilesMap {
88    if (!fs.existsSync(filePath)) {
89      return new Map();
90    }
91    NodeProcessorHelper.typeReferenceFileMap = new Map();
92    if (parserParam.getFileDir() === '') {
93      parserParam.setSdkPath(fileDir);
94      parserParam.setFileDir(path.resolve(fileDir, '..'));
95      parserParam.setRootNames([filePath]);
96      if (Parser.needLib) {
97        parserParam.setLibPath(path.resolve(FileUtils.getBaseDirName(), './libs'));
98      }
99      parserParam.setProgram();
100    }
101    parserParam.setFilePath(filePath);
102    let relFilePath: string = '';
103    relFilePath = path.relative(fileDir, filePath);
104
105    const program = parserParam.getTsProgram();
106    program.getTypeChecker();
107    const canonicalFileName = parserParam.compilerHost.getCanonicalFileName(filePath.replace(/\\/g, '/'));
108    const sourceFile: ts.SourceFile | undefined = program.getSourceFileByPath(canonicalFileName as ts.Path);
109    if (!sourceFile) {
110      return new Map();
111    }
112    const fileArr: Array<string> = [filePath];
113    sourceFile.statements.forEach((statement: ts.Statement) => {
114      if (ts.isImportDeclaration(statement) && statement.moduleSpecifier.getText().startsWith('./', 1)) {
115        fileArr.push(path.resolve(filePath, '..', statement.moduleSpecifier.getText().replace(/'|"/g, '')));
116      }
117    });
118    const sourceFileInfo: ApiInfo = new ApiInfo(ApiType.SOURCE_FILE, sourceFile, undefined);
119    sourceFileInfo.setFilePath(relFilePath);
120    sourceFileInfo.setFileAbsolutePath(filePath);
121    sourceFileInfo.setApiName(relFilePath);
122    sourceFileInfo.setIsStruct(filePath.endsWith(StringConstant.ETS_EXTENSION));
123    const currentApiMap: FileInfoMap = new Map();
124    currentApiMap.set(StringConstant.SELF, [sourceFileInfo]);
125    NodeProcessorHelper.processReference(sourceFile, currentApiMap, sourceFileInfo);
126    sourceFile.forEachChild((cNode: ts.Node) => {
127      NodeProcessorHelper.processNode(cNode, currentApiMap, sourceFileInfo);
128    });
129    if (!apiMap) {
130      apiMap = new Map();
131    }
132    apiMap.set(relFilePath, currentApiMap);
133    return apiMap;
134  }
135
136  /**
137   * 根据api节点的层级关系获取解析后的api对象
138   *
139   * @param { string[] } locations api节点的层级关系
140   * @param { FilesMap } apiMap api所在的map对象
141   * @returns { BasicApiInfo[] | undefined } 查询结果
142   */
143  static getApiInfo(locations: string[], apiMap: FilesMap, isAllSheet: boolean): BasicApiInfo[] {
144    const apiInfos: BasicApiInfo[] = [];
145    if (locations.length === 0) {
146      return apiInfos;
147    }
148    let currentMap: Map<string, object> = apiMap;
149    for (let i = 0; i < locations.length; i++) {
150      const key: string = locations[i];
151      const tempMap: object | undefined = currentMap.get(key);
152      if (!tempMap) {
153        return apiInfos;
154      }
155      currentMap = tempMap as Map<string, object>;
156    }
157    const currentMapValue: object | undefined = currentMap.get(StringConstant.SELF);
158    if (!currentMapValue) {
159      return apiInfos;
160    }
161    apiInfos.push(...(currentMapValue as BasicApiInfo[]));
162    if (isAllSheet) {
163      const isSameNameApi: boolean = DiffHelper.judgeIsSameNameFunction(apiInfos);
164      apiInfos.forEach((apiInfo: BasicApiInfo) => {
165        apiInfo.setIsSameNameFunction(isSameNameApi);
166      });
167    }
168    return apiInfos;
169  }
170
171  /**
172   * 该方法主要目的为了后续验证第一步基础解析的结果是否正确
173   *
174   * @param { FilesMap } apiMap 根据文件解析的map对象
175   * @returns { string } 返回解析后的字符串
176   */
177  static getParseResults(apiMap: FilesMap): string {
178    const clonedApiMap: FilesMap = _.cloneDeep(apiMap);
179    const apiResults: Map<string, BasicApiInfo[]> = new Map();
180    // map的第一层key均为文件路径名
181    for (const filePath of clonedApiMap.keys()) {
182      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
183      const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMap(fileMap);
184      apiResults.set(filePath, apis);
185    }
186    return JSON.stringify(Object.fromEntries(apiResults), null, 2);
187  }
188
189  static getParseEachSince(apiMap: FilesMap): string {
190    const clonedApiMap: FilesMap = _.cloneDeep(apiMap);
191    const apiResults: Map<string, Array<ResultsInfo.BasicApiInfo>> = new Map();
192    // map的第一层key均为文件路径名
193    for (const filePath of clonedApiMap.keys()) {
194      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
195      const sinceArr: Array<ResultsInfo.BasicApiInfo> = ResultsProcessHelper.processFileApiMapForEachSince(fileMap);
196      apiResults.set(filePath, sinceArr);
197      return JSON.stringify(sinceArr, null, 2);
198    }
199    return '';
200  }
201
202  /**
203   * 获取解析结果的所有api
204   *
205   * @param {FilesMap} parseResult 解析结果
206   * @return  {BasicApiInfo[]} 遍历平铺所有api
207   */
208  static getAllBasicApi(parseResult: FilesMap): BasicApiInfo[] {
209    let apiInfos: BasicApiInfo[] = [];
210    // map的第一层key均为文件路径名
211    for (const filePath of parseResult.keys()) {
212      const fileMap: FileInfoMap = parseResult.get(filePath) as FileInfoMap;
213      const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMapForGetBasicApi(fileMap);
214      apiInfos = apiInfos.concat(apis);
215    }
216    return apiInfos;
217  }
218}
219
220export type BasicApiInfoMap = Map<'_self', BasicApiInfo[]>;
221
222export type ApiInfosMap = Map<string, BasicApiInfoMap | BasicApiInfo[]>;
223
224export type FileInfoMap = Map<string, ApiInfosMap | BasicApiInfo[]>;
225
226export type FilesMap = Map<string, FileInfoMap>;
227