/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import fs from 'fs'; import path from 'path'; import ts from 'typescript'; import _ from 'lodash'; import { NodeProcessorHelper, parserParam } from './NodeProcessor'; import { ResultsProcessHelper } from './ResultsProcess'; import { ApiType, BasicApiInfo, ApiInfo } from '../../typedef/parser/ApiInfoDefination'; import { StringConstant } from '../../utils/Constant'; import { FileUtils } from '../../utils/FileUtils'; import { DiffHelper } from '../diff/diff'; import * as ResultsInfo from '../../typedef/parser/ResultsInfo'; /** * 解析d.ts工具类 */ export class Parser { static needLib: boolean = false; static cleanParserParamSDK(): void { parserParam.setFileDir(''); parserParam.setSdkPath(''); } /** * 根据传入的文件目录,对目录下的d.ts文件进行解析, * 并将其整合到一个map对象中 * * @param { string } fileDir 传入的文件目录 * @param { string } [collectFile = ''] 统计的文件或目录 * @returns { FilesMap } 返回解析后的map对象 */ static parseDir(fileDir: string, collectFile: string = ''): FilesMap { Parser.cleanParserParamSDK(); const files: Array = FileUtils.readFilesInDir(fileDir, (name) => { return name.endsWith(StringConstant.DTS_EXTENSION) || name.endsWith(StringConstant.DETS_EXTENSION); }); const apiMap: FilesMap = new Map(); let collectFiles: Array = []; if (collectFile === '') { parserParam.setSdkPath(fileDir); collectFiles = files; } else if (FileUtils.isDirectory(collectFile)) { Parser.needLib = true; parserParam.setSdkPath(collectFile); collectFiles = FileUtils.readFilesInDir(collectFile, (name) => { return name.endsWith(StringConstant.DTS_EXTENSION) || name.endsWith(StringConstant.DETS_EXTENSION); }); } else if (FileUtils.isFile(collectFile)) { Parser.needLib = true; parserParam.setSdkPath(path.resolve(collectFile, '..')); collectFiles = [collectFile]; } parserParam.setFileDir(fileDir); parserParam.setRootNames(collectFiles); if (Parser.needLib) { parserParam.setLibPath(path.resolve(FileUtils.getBaseDirName(), './libs')); } parserParam.setProgram(); collectFiles.forEach((filePath: string) => { Parser.parseFile(fileDir, filePath, apiMap); }); return apiMap; } /** * 根据文件名解析api信息 * * @param { string } fileDir 文件的跟路径 * @param { string } filePath 文件路径名 * @param { FilesMap } [apiMap] 返回处理后的存储api信息的map对象 * @returns { FilesMap } 返回处理后的存储api信息的map对象 */ static parseFile(fileDir: string, filePath: string, apiMap?: FilesMap): FilesMap { if (!fs.existsSync(filePath)) { return new Map(); } NodeProcessorHelper.typeReferenceFileMap = new Map(); if (parserParam.getFileDir() === '') { parserParam.setSdkPath(fileDir); parserParam.setFileDir(path.resolve(fileDir, '..')); parserParam.setRootNames([filePath]); if (Parser.needLib) { parserParam.setLibPath(path.resolve(FileUtils.getBaseDirName(), './libs')); } parserParam.setProgram(); } parserParam.setFilePath(filePath); let relFilePath: string = ''; relFilePath = path.relative(fileDir, filePath); const program = parserParam.getTsProgram(); program.getTypeChecker(); const canonicalFileName = parserParam.compilerHost.getCanonicalFileName(filePath.replace(/\\/g, '/')); const sourceFile: ts.SourceFile | undefined = program.getSourceFileByPath(canonicalFileName as ts.Path); if (!sourceFile) { return new Map(); } const fileArr: Array = [filePath]; sourceFile.statements.forEach((statement: ts.Statement) => { if (ts.isImportDeclaration(statement) && statement.moduleSpecifier.getText().startsWith('./', 1)) { fileArr.push(path.resolve(filePath, '..', statement.moduleSpecifier.getText().replace(/'|"/g, ''))); } }); const sourceFileInfo: ApiInfo = new ApiInfo(ApiType.SOURCE_FILE, sourceFile, undefined); sourceFileInfo.setFilePath(relFilePath); sourceFileInfo.setFileAbsolutePath(filePath); sourceFileInfo.setApiName(relFilePath); sourceFileInfo.setIsStruct(filePath.endsWith(StringConstant.ETS_EXTENSION)); const currentApiMap: FileInfoMap = new Map(); currentApiMap.set(StringConstant.SELF, [sourceFileInfo]); NodeProcessorHelper.processReference(sourceFile, currentApiMap, sourceFileInfo); sourceFile.forEachChild((cNode: ts.Node) => { NodeProcessorHelper.processNode(cNode, currentApiMap, sourceFileInfo); }); if (!apiMap) { apiMap = new Map(); } apiMap.set(relFilePath, currentApiMap); return apiMap; } /** * 根据api节点的层级关系获取解析后的api对象 * * @param { string[] } locations api节点的层级关系 * @param { FilesMap } apiMap api所在的map对象 * @returns { BasicApiInfo[] | undefined } 查询结果 */ static getApiInfo(locations: string[], apiMap: FilesMap, isAllSheet: boolean): BasicApiInfo[] { const apiInfos: BasicApiInfo[] = []; if (locations.length === 0) { return apiInfos; } let currentMap: Map = apiMap; for (let i = 0; i < locations.length; i++) { const key: string = locations[i]; const tempMap: object | undefined = currentMap.get(key); if (!tempMap) { return apiInfos; } currentMap = tempMap as Map; } const currentMapValue: object | undefined = currentMap.get(StringConstant.SELF); if (!currentMapValue) { return apiInfos; } apiInfos.push(...(currentMapValue as BasicApiInfo[])); if (isAllSheet) { const isSameNameApi: boolean = DiffHelper.judgeIsSameNameFunction(apiInfos); apiInfos.forEach((apiInfo: BasicApiInfo) => { apiInfo.setIsSameNameFunction(isSameNameApi); }); } return apiInfos; } /** * 该方法主要目的为了后续验证第一步基础解析的结果是否正确 * * @param { FilesMap } apiMap 根据文件解析的map对象 * @returns { string } 返回解析后的字符串 */ static getParseResults(apiMap: FilesMap): string { const clonedApiMap: FilesMap = _.cloneDeep(apiMap); const apiResults: Map = new Map(); // map的第一层key均为文件路径名 for (const filePath of clonedApiMap.keys()) { const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap; const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMap(fileMap); apiResults.set(filePath, apis); } return JSON.stringify(Object.fromEntries(apiResults), null, 2); } static getParseEachSince(apiMap: FilesMap): string { const clonedApiMap: FilesMap = _.cloneDeep(apiMap); const apiResults: Map> = new Map(); // map的第一层key均为文件路径名 for (const filePath of clonedApiMap.keys()) { const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap; const sinceArr: Array = ResultsProcessHelper.processFileApiMapForEachSince(fileMap); apiResults.set(filePath, sinceArr); return JSON.stringify(sinceArr, null, 2); } return ''; } /** * 获取解析结果的所有api * * @param {FilesMap} parseResult 解析结果 * @return {BasicApiInfo[]} 遍历平铺所有api */ static getAllBasicApi(parseResult: FilesMap): BasicApiInfo[] { let apiInfos: BasicApiInfo[] = []; // map的第一层key均为文件路径名 for (const filePath of parseResult.keys()) { const fileMap: FileInfoMap = parseResult.get(filePath) as FileInfoMap; const apis: BasicApiInfo[] = ResultsProcessHelper.processFileApiMapForGetBasicApi(fileMap); apiInfos = apiInfos.concat(apis); } return apiInfos; } } export type BasicApiInfoMap = Map<'_self', BasicApiInfo[]>; export type ApiInfosMap = Map; export type FileInfoMap = Map; export type FilesMap = Map;