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