1/* 2 * Copyright (c) 2024 - 2025 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 { fetchDependenciesFromFile, OH_PACKAGE_JSON5 } from 'arkanalyzer'; 17import type { DependsNode } from 'arkanalyzer/lib/core/graph/DependsGraph'; 18import { Module, ModuleCategory, ModuleDepsGraph } from './moduleComponent'; 19import path from 'path'; 20import fs from 'fs'; 21import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger'; 22import { getModuleKind, OH_MODULES_DIR } from './utils'; 23 24const logger = Logger.getLogger(LOG_MODULE_TYPE.TOOL, 'moduleDeps'); 25 26export class ModuleDeps { 27 private static instance: ModuleDeps; 28 29 private constructor() { 30 } 31 32 public static getInstance(): ModuleDeps { 33 if (!this.instance) { 34 this.instance = new ModuleDeps(); 35 } 36 return this.instance; 37 } 38 39 public addDeps(depsGraph: ModuleDepsGraph, src: DependsNode<Module>): void { 40 const moduleOhPkgPath = path.join(src.getNodeAttr().name, OH_PACKAGE_JSON5); 41 if (moduleOhPkgPath) { 42 const dstNodes: Module[] = this.getDstDeps(moduleOhPkgPath); 43 dstNodes.forEach((dstNode) => { 44 if (!depsGraph.hasDepsNode(dstNode.name)) { 45 const dst = depsGraph.addDepsNode(dstNode.name, dstNode); 46 const edge = depsGraph.addEdge(src, dst, { kind: 0 }); 47 this.addDeps(depsGraph, dst); 48 } 49 }); 50 } 51 } 52 53 private getDstDeps(ohPkgPath: string): Module[] { 54 let dstDeps: Module[] = []; 55 const ohContent = fetchDependenciesFromFile(ohPkgPath); 56 let depMaps: Map<string, string> = new Map(); 57 58 if (ohContent && ohContent.dependencies) { 59 Object.entries(ohContent.dependencies).forEach(([name, value]) => { 60 dstDeps.push(this.genDstNode(name, value as string, ohPkgPath)); 61 }); 62 } 63 64 return dstDeps; 65 } 66 67 private genDstNode(key: string, value: string, ohPkgPath: string): Module { 68 if (value.startsWith('tag:')) { 69 return { 70 name: path.join(path.dirname(ohPkgPath), OH_MODULES_DIR, key), 71 kind: ModuleCategory.TAGGED_PACKAGE, 72 tag: value.replace(/^tag:/, ''), 73 }; 74 } else if (/^(file:|\.\/|\.\.\/)/.test(value)) { 75 return this.handleLocal(key, value.replace(/^file:/, ''), ohPkgPath); 76 } else if (/^[~^0-9]/.test(value)) { 77 return { 78 name: path.join(path.dirname(ohPkgPath), OH_MODULES_DIR, key), 79 kind: ModuleCategory.THIRD_PARTY_PACKAGE, 80 tag: value, 81 }; 82 } else { 83 return { 84 name: `Unknown: key is ${key}, value is ${value}, module path is ${path.dirname(ohPkgPath)}.`, 85 kind: ModuleCategory.UNKNOWN, 86 }; 87 } 88 } 89 90 private handleLocal(moduleName: string, modulePath: string, ohPkgPath: string): Module { 91 const moduleInstalledPath = path.join(path.dirname(ohPkgPath), OH_MODULES_DIR, moduleName); 92 const originPkgPath = path.join(path.dirname(ohPkgPath), modulePath); 93 const isDir = fs.statSync(originPkgPath).isDirectory(); 94 let moduleKind = getModuleKind(moduleInstalledPath); 95 if (moduleKind === ModuleCategory.UNKNOWN && (modulePath.endsWith('.hsp') || modulePath.endsWith('.tgz'))) { 96 moduleKind = ModuleCategory.HSP; 97 } else if (moduleKind === ModuleCategory.UNKNOWN && (modulePath.endsWith('.har'))) { 98 moduleKind = ModuleCategory.HAR; 99 } 100 return { 101 name: isDir ? originPkgPath : moduleInstalledPath, 102 kind: moduleKind, 103 originPath: isDir ? undefined : originPkgPath, 104 }; 105 } 106}