• 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 } from './NodeProcessor';
22import { ResultsProcessHelper } from './ResultsProcess';
23import { ApiType, BasicApiInfo } from '../../typedef/parser/ApiInfoDefination';
24import { StringConstant } from '../../utils/Constant';
25import { FileUtils } from '../../utils/FileUtils';
26import * as ResultsInfo from '../../typedef/parser/ResultsInfo';
27
28/**
29 * 解析d.ts工具类
30 */
31export class Parser {
32  /**
33   * 根据传入的文件目录,对目录下的d.ts文件进行解析,
34   * 并将其整合到一个map对象中
35   *
36   * @param { string } fileDir 传入的文件目录
37   * @returns { FilesMap } 返回解析后的map对象
38   */
39  static parseDir(fileDir: string): FilesMap {
40    const files: Array<string> = FileUtils.readFilesInDir(fileDir, (name) => {
41      return name.endsWith(StringConstant.DTS_EXTENSION) || name.endsWith(StringConstant.DETS_EXTENSION);
42    });
43    const apiMap: FilesMap = new Map();
44    files.forEach((filePath: string) => {
45      Parser.parseFile(fileDir, filePath, apiMap);
46    });
47    return apiMap;
48  }
49
50  /**
51   * 根据文件名解析api信息
52   *
53   * @param { string } fileDir 文件的跟路径
54   * @param { string } filePath 文件路径名
55   * @param { FilesMap } [apiMap] 返回处理后的存储api信息的map对象
56   * @returns { FilesMap } 返回处理后的存储api信息的map对象
57   */
58  static parseFile(fileDir: string, filePath: string, apiMap?: FilesMap): FilesMap {
59    if (!fs.existsSync(filePath)) {
60      return new Map();
61    }
62    const fileContent: string = fs.readFileSync(filePath, StringConstant.UTF8);
63    let relFilePath: string = '';
64    relFilePath = path.relative(fileDir, filePath);
65    const fileName = path
66      .basename(filePath)
67      .replace(new RegExp(StringConstant.DTS_EXTENSION, 'g'), StringConstant.TS_EXTENSION)
68      .replace(new RegExp(StringConstant.DETS_EXTENSION, 'g'), StringConstant.ETS_EXTENSION);
69    const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, fileContent, ts.ScriptTarget.ES2017, true);
70    const sourceFileInfo: BasicApiInfo = new BasicApiInfo(ApiType.SOURCE_FILE, sourceFile, undefined);
71    sourceFileInfo.setFilePath(relFilePath);
72    sourceFileInfo.setApiName(relFilePath);
73    sourceFileInfo.setIsStruct(filePath.endsWith(StringConstant.ETS_EXTENSION));
74    const currentApiMap: FileInfoMap = new Map();
75    currentApiMap.set(StringConstant.SELF, [sourceFileInfo]);
76    NodeProcessorHelper.processReference(sourceFile, currentApiMap, sourceFileInfo);
77    sourceFile.forEachChild((cNode: ts.Node) => {
78      NodeProcessorHelper.processNode(cNode, currentApiMap, sourceFileInfo);
79    });
80    if (!apiMap) {
81      apiMap = new Map();
82    }
83    apiMap.set(relFilePath, currentApiMap);
84    return apiMap;
85  }
86
87  /**
88   * 根据api节点的层级关系获取解析后的api对象
89   *
90   * @param { string[] } locations api节点的层级关系
91   * @param { FilesMap } apiMap api所在的map对象
92   * @returns { BasicApiInfo[] | undefined } 查询结果
93   */
94  static getApiInfo(locations: string[], apiMap: FilesMap): BasicApiInfo[] {
95    const apiInfos: BasicApiInfo[] = [];
96    if (locations.length === 0) {
97      return apiInfos;
98    }
99    let currentMap: Map<string, object> = apiMap;
100    for (let i = 0; i < locations.length; i++) {
101      const key: string = locations[i];
102      const tempMap: object | undefined = currentMap.get(key);
103      if (!tempMap) {
104        return apiInfos;
105      }
106      currentMap = tempMap as Map<string, object>;
107    }
108    const currentMapValue: object | undefined = currentMap.get(StringConstant.SELF);
109    if (!currentMapValue) {
110      return apiInfos;
111    }
112    apiInfos.push(...(currentMapValue as BasicApiInfo[]));
113    return apiInfos;
114  }
115
116  /**
117   * 该方法主要目的为了后续验证第一步基础解析的结果是否正确
118   *
119   * @param { FilesMap } apiMap 根据文件解析的map对象
120   * @returns { string } 返回解析后的字符串
121   */
122  static getParseResults(apiMap: FilesMap): string {
123    const clonedApiMap: FilesMap = _.cloneDeep(apiMap);
124    const apiResults: Map<string, BasicApiInfo[]> = new Map();
125    // map的第一层key均为文件路径名
126    for (const filePath of clonedApiMap.keys()) {
127      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
128      const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMap(fileMap);
129      apiResults.set(filePath, apis);
130    }
131    return JSON.stringify(Object.fromEntries(apiResults), null, 2);
132  }
133
134  static getParseEachSince(apiMap: FilesMap): string {
135    const clonedApiMap: FilesMap = _.cloneDeep(apiMap);
136    const apiResults: Map<string, Array<ResultsInfo.BasicApiInfo>> = new Map();
137    // map的第一层key均为文件路径名
138    for (const filePath of clonedApiMap.keys()) {
139      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
140      const sinceArr: Array<ResultsInfo.BasicApiInfo> = ResultsProcessHelper.processFileApiMapForEachSince(fileMap);
141      apiResults.set(filePath, sinceArr);
142      return JSON.stringify(sinceArr, null, 2);
143    }
144    return '';
145  }
146
147  /**
148   * 获取解析结果的所有api
149   *
150   * @param {FilesMap} parseResult 解析结果
151   * @return  {BasicApiInfo[]} 遍历平铺所有api
152   */
153  static getAllBasicApi(parseResult: FilesMap): BasicApiInfo[] {
154    let apiInfos: BasicApiInfo[] = [];
155    // map的第一层key均为文件路径名
156    for (const filePath of parseResult.keys()) {
157      const fileMap: FileInfoMap = parseResult.get(filePath) as FileInfoMap;
158      const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMapForGetBasicApi(fileMap);
159      apiInfos = apiInfos.concat(apis);
160    }
161    return apiInfos;
162  }
163}
164
165export type BasicApiInfoMap = Map<'_self', BasicApiInfo[]>;
166
167export type ApiInfosMap = Map<string, BasicApiInfoMap | BasicApiInfo[]>;
168
169export type FileInfoMap = Map<string, ApiInfosMap | BasicApiInfo[]>;
170
171export type FilesMap = Map<string, FileInfoMap>;
172