• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}