• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023-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 path from 'path';
17import fs from 'fs';
18import type sourceMap from 'source-map';
19
20import { minify, MinifyOutput } from 'terser';
21import {
22  endFilesEvent,
23  endSingleFileEvent,
24  EventList,
25  MemoryUtils,
26  nameCacheMap,
27  performancePrinter,
28  ProjectCollections,
29  startFilesEvent,
30  startSingleFileEvent,
31} from 'arkguard';
32import type { HvigorErrorInfo } from 'arkguard';
33import {
34  OH_MODULES,
35  SEPARATOR_AT,
36  SEPARATOR_BITWISE_AND,
37  SEPARATOR_SLASH
38} from './fast_build/ark_compiler/common/ark_define';
39import {
40  ARKTS_MODULE_NAME,
41  PACKAGES,
42  TEMPORARY,
43  ZERO,
44  ONE,
45  EXTNAME_JS,
46  EXTNAME_TS,
47  EXTNAME_MJS,
48  EXTNAME_CJS,
49  EXTNAME_ABC,
50  EXTNAME_ETS,
51  EXTNAME_D_ETS,
52  EXTNAME_TS_MAP,
53  EXTNAME_JS_MAP,
54  ESMODULE,
55  FAIL,
56  TS2ABC,
57  ES2ABC,
58  EXTNAME_PROTO_BIN,
59  NATIVE_MODULE
60} from './pre_define';
61import {
62  isMac,
63  isWindows,
64  isPackageModulesFile,
65  genTemporaryPath,
66  getExtensionIfUnfullySpecifiedFilepath,
67  mkdirsSync,
68  toUnixPath,
69  validateFilePathLength,
70  harFilesRecord,
71  getProjectRootPath
72} from './utils';
73import type { GeneratedFileInHar } from './utils';
74import {
75  extendSdkConfigs,
76  projectConfig,
77  sdkConfigPrefix
78} from '../main';
79import {
80  getRelativeSourcePath,
81  mangleFilePath,
82  setNewNameCache,
83  getNameCacheByPath,
84  setUnobfuscationNames,
85  writeObfuscatedFile
86} from './fast_build/ark_compiler/common/ob_config_resolver';
87import { moduleRequestCallback } from './fast_build/system_api/api_check_utils';
88import { SourceMapGenerator } from './fast_build/ark_compiler/generate_sourcemap';
89import { sourceFileBelongProject } from './fast_build/ark_compiler/module/module_source_file';
90import { MemoryMonitor } from './fast_build/meomry_monitor/rollup-plugin-memory-monitor';
91import { MemoryDefine } from './fast_build/meomry_monitor/memory_define';
92import {
93  ArkTSInternalErrorDescription,
94  ArkTSErrorDescription,
95  ErrorCode
96} from './fast_build/ark_compiler/error_code';
97import {
98  LogData,
99  LogDataFactory
100} from './fast_build/ark_compiler/logger';
101import * as ts from 'typescript';
102import {
103  PreloadFileModules
104} from './fast_build/ark_compiler/module/module_preload_file_utils';
105
106export const SRC_MAIN: string = 'src/main';
107
108export let newSourceMaps: Object = {};
109
110export const packageCollection: Map<string, Array<string>> = new Map();
111// Splicing ohmurl or record name based on filePath and context information table.
112export function getNormalizedOhmUrlByFilepath(filePath: string, projectConfig: Object, logger: Object,
113  pkgParams: Object, importerFile: string): string {
114  const { pkgName, pkgPath, isRecordName } = pkgParams;
115  const ohmurlInfo: Object = getPkgInfo(filePath, projectConfig, logger, pkgPath, pkgName, importerFile);
116  if (!ohmurlInfo) {
117    return filePath;
118  }
119  const { projectFilePath, pkgInfo } = ohmurlInfo;
120  const recordName: string = `${pkgInfo.bundleName}&${pkgName}/${projectFilePath}&${pkgInfo.version}`;
121  if (isRecordName) {
122    // record name style: <bunldName>&<packageName>/entry/ets/xxx/yyy&<version>
123    return recordName;
124  }
125  return `${pkgInfo.isSO ? 'Y' : 'N'}&${pkgInfo.moduleName}&${recordName}`;
126}
127
128export function getPkgInfo(filePath: string, projectConfig: Object, logger: Object, pkgPath: string,
129  pkgName: string, importerFile?: string): Object {
130  // rollup uses commonjs plugin to handle commonjs files,
131  // the commonjs files are prefixed with '\x00' and need to be removed.
132  if (filePath.startsWith('\x00')) {
133    filePath = filePath.replace('\x00', '');
134  }
135  let unixFilePath: string = toUnixPath(filePath);
136  unixFilePath = unixFilePath.substring(0, filePath.lastIndexOf('.')); // remove extension
137  // case1: /entry/src/main/ets/xxx/yyy
138  // case2: /entry/src/ohosTest/ets/xxx/yyy
139  // case3: /node_modules/xxx/yyy
140  // case4: /entry/node_modules/xxx/yyy
141  // case5: /library/node_modules/xxx/yyy
142  // case6: /library/index.ts
143  // ---> @normalized:N&<moduleName>&<bunldName>&<packageName>/entry/ets/xxx/yyy&<version>
144  let pkgInfo = projectConfig.pkgContextInfo[pkgName];
145  if (!pkgInfo || !pkgPath) {
146    const errInfo: LogData = LogDataFactory.newInstance(
147      ErrorCode.ETS2BUNDLE_EXTERNAL_FAILED_TO_RESOLVE_OHM_URL,
148      ArkTSErrorDescription,
149      'Failed to resolve OhmUrl. ' +
150      `Failed to get a resolved OhmUrl for "${filePath}" imported by "${importerFile}".`,
151      '',
152      [`Check whether the "${pkgName}" module which ${filePath} belongs to is correctly configured.`,
153       `Check if the corresponding file name "${filePath}" is correct(including case-sensitivity).`]
154    );
155    logger.printError(errInfo);
156    logger.returnErrorFileId(importerFile);
157    return undefined;
158  }
159  const projectFilePath: string = unixFilePath.replace(toUnixPath(pkgPath) + '/', '');
160  return { projectFilePath, pkgInfo };
161}
162
163export function getOhmUrlByFilepath(filePath: string, projectConfig: Object, logger: Object, namespace?: string,
164  importerFile?: string): string {
165  // remove '\x00' from the rollup virtual commonjs file's filePath
166  if (filePath.startsWith('\x00')) {
167    filePath = filePath.replace('\x00', '');
168  }
169  let unixFilePath: string = toUnixPath(filePath);
170  unixFilePath = unixFilePath.substring(0, filePath.lastIndexOf('.')); // remove extension
171  const REG_PROJECT_SRC: RegExp = /(\S+)\/src\/(?:main|ohosTest)\/(ets|js|mock)\/(\S+)/;
172
173  const packageInfo: string[] = getPackageInfo(projectConfig.aceModuleJsonPath);
174  const bundleName: string = packageInfo[0];
175  const moduleName: string = packageInfo[1];
176  const moduleRootPath: string = toUnixPath(projectConfig.modulePathMap[moduleName]);
177  const projectRootPath: string = toUnixPath(getProjectRootPath(filePath, projectConfig, projectConfig?.rootPathSet));
178  // case1: /entry/src/main/ets/xxx/yyy     ---> @bundle:<bundleName>/entry/ets/xxx/yyy
179  // case2: /entry/src/ohosTest/ets/xxx/yyy ---> @bundle:<bundleName>/entry_test@entry/ets/xxx/yyy
180  // case3: /node_modules/xxx/yyy           ---> @package:pkg_modules/xxx/yyy
181  // case4: /entry/node_modules/xxx/yyy     ---> @package:pkg_modules@entry/xxx/yyy
182  // case5: /library/node_modules/xxx/yyy   ---> @package:pkg_modules@library/xxx/yyy
183  // case6: /library/index.ts               ---> @bundle:<bundleName>/library/index
184  const projectFilePath: string = unixFilePath.replace(projectRootPath, '');
185  const packageDir: string = projectConfig.packageDir;
186  const result: RegExpMatchArray | null = projectFilePath.match(REG_PROJECT_SRC);
187  if (result && result[1].indexOf(packageDir) === -1) {
188    const relativePath = processSrcMain(result, projectFilePath);
189    if (namespace && moduleName !== namespace) {
190      return `${bundleName}/${moduleName}@${namespace}/${relativePath}`;
191    }
192    return `${bundleName}/${moduleName}/${relativePath}`;
193  }
194
195  const processParams: Object = {
196    projectFilePath,
197    unixFilePath,
198    packageDir,
199    projectRootPath,
200    moduleRootPath,
201    projectConfig,
202    namespace,
203    logger,
204    importerFile,
205    originalFilePath: filePath
206  };
207  return processPackageDir(processParams);
208}
209
210function processSrcMain(result: RegExpMatchArray | null, projectFilePath: string): string {
211  let langType: string = result[2];
212  let relativePath: string = result[3];
213  // case7: /entry/src/main/ets/xxx/src/main/js/yyy ---> @bundle:<bundleName>/entry/ets/xxx/src/main/js/yyy
214  const REG_SRC_MAIN: RegExp = /src\/(?:main|ohosTest)\/(ets|js)\//;
215  const srcMainIndex: number = result[1].search(REG_SRC_MAIN);
216  if (srcMainIndex !== -1) {
217    relativePath = projectFilePath.substring(srcMainIndex).replace(REG_SRC_MAIN, '');
218    langType = projectFilePath.replace(relativePath, '').match(REG_SRC_MAIN)[1];
219  }
220  return `${langType}/${relativePath}`;
221}
222
223function processPackageDir(params: Object): string {
224  const { projectFilePath, unixFilePath, packageDir, projectRootPath, moduleRootPath,
225    projectConfig, namespace, logger, importerFile, originalFilePath } = params;
226  if (projectFilePath.indexOf(packageDir) !== -1) {
227    if (compileToolIsRollUp()) {
228      const tryProjectPkg: string = toUnixPath(path.join(projectRootPath, packageDir));
229      if (unixFilePath.indexOf(tryProjectPkg) !== -1) {
230        return unixFilePath.replace(tryProjectPkg, `${packageDir}`).replace(new RegExp(packageDir, 'g'), PACKAGES);
231      }
232
233      // iterate the modulePathMap to find the module which contains the pkg_module's file
234      for (const moduleName in projectConfig.modulePathMap) {
235        const modulePath: string = projectConfig.modulePathMap[moduleName];
236        const tryModulePkg: string = toUnixPath(path.resolve(modulePath, packageDir));
237        if (unixFilePath.indexOf(tryModulePkg) !== -1) {
238          return unixFilePath.replace(tryModulePkg, `${packageDir}@${moduleName}`).replace(new RegExp(packageDir, 'g'), PACKAGES);
239        }
240      }
241
242      const errInfo: LogData = LogDataFactory.newInstance(
243        ErrorCode.ETS2BUNDLE_EXTERNAL_FAILED_TO_RESOLVE_OHM_URL,
244        ArkTSErrorDescription,
245        'Failed to resolve OhmUrl. ' +
246        `Failed to get a resolved OhmUrl for "${originalFilePath}" imported by "${importerFile}".`,
247        '',
248        [`Check whether the module which ${originalFilePath} belongs to is correctly configured.`,
249         `Check if the corresponding file name "${originalFilePath}" is correct(including case-sensitivity).`]
250      );
251      logger.printError(errInfo);
252      logger.returnErrorFileId(importerFile);
253      return originalFilePath;
254    }
255
256    // webpack with old implematation
257    const tryProjectPkg: string = toUnixPath(path.join(projectRootPath, packageDir));
258    if (unixFilePath.indexOf(tryProjectPkg) !== -1) {
259      return unixFilePath.replace(tryProjectPkg, `${packageDir}/${ONE}`).replace(new RegExp(packageDir, 'g'), PACKAGES);
260    }
261
262    const tryModulePkg: string = toUnixPath(path.join(moduleRootPath, packageDir));
263    if (unixFilePath.indexOf(tryModulePkg) !== -1) {
264      return unixFilePath.replace(tryModulePkg, `${packageDir}/${ZERO}`).replace(new RegExp(packageDir, 'g'), PACKAGES);
265    }
266  }
267
268  const packageInfo: string[] = getPackageInfo(projectConfig.aceModuleJsonPath);
269  const bundleName: string = packageInfo[0];
270  const moduleName: string = packageInfo[1];
271  for (const key in projectConfig.modulePathMap) {
272    const moduleRootPath: string = toUnixPath(projectConfig.modulePathMap[key]);
273    if (unixFilePath.indexOf(moduleRootPath + '/') !== -1) {
274      const relativeModulePath: string = unixFilePath.replace(moduleRootPath + '/', '');
275      if (namespace && moduleName !== namespace) {
276        return `${bundleName}/${moduleName}@${namespace}/${relativeModulePath}`;
277      }
278      return `${bundleName}/${moduleName}/${relativeModulePath}`;
279    }
280  }
281
282  const errInfo: LogData = LogDataFactory.newInstance(
283    ErrorCode.ETS2BUNDLE_EXTERNAL_FAILED_TO_RESOLVE_OHM_URL,
284    ArkTSErrorDescription,
285    'Failed to resolve OhmUrl. ' +
286    `Failed to get a resolved OhmUrl for "${originalFilePath}" imported by "${importerFile}".`,
287    '',
288    [`Check whether the module which ${originalFilePath} belongs to is correctly configured.`,
289     `Check if the corresponding file name "${originalFilePath}" is correct(including case-sensitivity).`]
290  );
291  logger.printError(errInfo);
292  logger.returnErrorFileId(importerFile);
293  return originalFilePath;
294}
295
296export type OhmUrlParams = {
297  moduleRequest: string;
298  moduleId: string;
299  config?: Object;
300  logger?: Object;
301  importerFile?: string;
302};
303export function getOhmUrlBySystemApiOrLibRequest(params: OhmUrlParams,
304  useNormalizedOHMUrl: boolean = false, needPreloadSo: boolean = false): string {
305  // 'arkui-x' represents cross platform related APIs, processed as 'ohos'
306  const REG_SYSTEM_MODULE: RegExp = new RegExp(`@(${sdkConfigPrefix})\\.(\\S+)`);
307  const REG_LIB_SO: RegExp = /lib(\S+)\.so/;
308  const moduleRequest = params.moduleRequest;
309  if (REG_SYSTEM_MODULE.test(moduleRequest.trim())) {
310    return moduleRequest.replace(REG_SYSTEM_MODULE, (_, moduleType, systemKey) => {
311      let moduleRequestStr = '';
312      if (extendSdkConfigs) {
313        moduleRequestStr = moduleRequestCallback(moduleRequest, _, moduleType, systemKey);
314      }
315      if (moduleRequestStr !== '') {
316        needPreloadSo && PreloadFileModules.updatePreloadFileDataByItems(
317          moduleRequest, moduleRequestStr, params.moduleId);
318        return moduleRequestStr;
319      }
320      let resultOhmUrl: string = '';
321      const systemModule: string = `${moduleType}.${systemKey}`;
322      if (NATIVE_MODULE.has(systemModule)) {
323        resultOhmUrl = `@native:${systemModule}`;
324      } else if (moduleType === ARKTS_MODULE_NAME) {
325        // @arkts.xxx -> @ohos:arkts.xxx
326        resultOhmUrl = `@ohos:${systemModule}`;
327      } else {
328        resultOhmUrl = `@ohos:${systemKey}`;
329      }
330      needPreloadSo && PreloadFileModules.updatePreloadFileDataByItems(moduleRequest, resultOhmUrl, params.moduleId);
331      return resultOhmUrl;
332    });
333  }
334  if (REG_LIB_SO.test(moduleRequest.trim())) {
335    if (useNormalizedOHMUrl) {
336      const pkgInfo = params.config.pkgContextInfo[moduleRequest];
337      if (!pkgInfo) {
338        const errInfo: LogData = LogDataFactory.newInstance(
339          ErrorCode.ETS2BUNDLE_INTERNAL_UNABLE_TO_GET_PKG_CONTENT_INFO,
340          ArkTSInternalErrorDescription,
341          `Can not get pkgContextInfo of package '${moduleRequest}' ` +
342          `which being imported by '${params.importerFile}'`
343        );
344        params.logger?.printError(errInfo);
345        return moduleRequest;
346      }
347      const isSo = pkgInfo.isSO ? 'Y' : 'N';
348      return `@normalized:${isSo}&${pkgInfo.moduleName}&${pkgInfo.bundleName}&${moduleRequest}&${pkgInfo.version}`;
349    }
350    return moduleRequest.replace(REG_LIB_SO, (_, libsoKey) => {
351      return `@app:${projectConfig.bundleName}/${projectConfig.moduleName}/${libsoKey}`;
352    });
353  }
354  return undefined;
355}
356
357export function genSourceMapFileName(temporaryFile: string): string {
358  let abcFile: string = temporaryFile;
359  if (temporaryFile.endsWith(EXTNAME_TS)) {
360    abcFile = temporaryFile.replace(/\.ts$/, EXTNAME_TS_MAP);
361  } else {
362    abcFile = temporaryFile.replace(/\.js$/, EXTNAME_JS_MAP);
363  }
364  return abcFile;
365}
366
367export function getBuildModeInLowerCase(projectConfig: Object): string {
368  return (compileToolIsRollUp() ? projectConfig.buildMode : projectConfig.buildArkMode).toLowerCase();
369}
370
371/**
372 * This Api only used by webpack compiling process - js-loader
373 * @param sourcePath The path in build cache dir
374 * @param sourceCode The intermediate js source code
375 */
376export function writeFileSyncByString(sourcePath: string, sourceCode: string, projectConfig: Object, logger: Object): void {
377  const filePath: string = genTemporaryPath(sourcePath, projectConfig.projectPath, process.env.cachePath,
378    projectConfig, undefined);
379  if (filePath.length === 0) {
380    return;
381  }
382  mkdirsSync(path.dirname(filePath));
383  if (/\.js$/.test(sourcePath)) {
384    sourceCode = transformModuleSpecifier(sourcePath, sourceCode, projectConfig);
385    if (projectConfig.buildArkMode === 'debug') {
386      fs.writeFileSync(filePath, sourceCode);
387      return;
388    }
389    writeObfuscatedSourceCode({content: sourceCode, buildFilePath: filePath, relativeSourceFilePath: ''},
390      logger, projectConfig);
391  }
392  if (/\.json$/.test(sourcePath)) {
393    fs.writeFileSync(filePath, sourceCode);
394  }
395}
396
397export function transformModuleSpecifier(sourcePath: string, sourceCode: string, projectConfig: Object): string {
398  // replace relative moduleSpecifier with ohmURl
399  const REG_RELATIVE_DEPENDENCY: RegExp = /(?:import|from)(?:\s*)['"]((?:\.\/|\.\.\/)[^'"]+|(?:\.\/?|\.\.\/?))['"]/g;
400  const REG_HAR_DEPENDENCY: RegExp = /(?:import|from)(?:\s*)['"]([^\.\/][^'"]+)['"]/g;
401  // replace requireNapi and requireNativeModule with import
402  const REG_REQUIRE_NATIVE_MODULE: RegExp = /var (\S+) = globalThis.requireNativeModule\(['"](\S+)['"]\);/g;
403  const REG_REQUIRE_NAPI_APP: RegExp = /var (\S+) = globalThis.requireNapi\(['"](\S+)['"], true, ['"](\S+)['"]\);/g;
404  const REG_REQUIRE_NAPI_OHOS: RegExp = /var (\S+) = globalThis.requireNapi\(['"](\S+)['"]\);/g;
405
406  return sourceCode.replace(REG_HAR_DEPENDENCY, (item, moduleRequest) => {
407    return replaceHarDependency(item, moduleRequest, projectConfig);
408  }).replace(REG_RELATIVE_DEPENDENCY, (item, moduleRequest) => {
409    return replaceRelativeDependency(item, moduleRequest, toUnixPath(sourcePath), projectConfig);
410  }).replace(REG_REQUIRE_NATIVE_MODULE, (_, moduleRequest, moduleName) => {
411    return `import ${moduleRequest} from '@native:${moduleName}';`;
412  }).replace(REG_REQUIRE_NAPI_APP, (_, moduleRequest, soName, moudlePath) => {
413    return `import ${moduleRequest} from '@app:${moudlePath}/${soName}';`;
414  }).replace(REG_REQUIRE_NAPI_OHOS, (_, moduleRequest, moduleName) => {
415    return `import ${moduleRequest} from '@ohos:${moduleName}';`;
416  });
417}
418
419function removeSuffix(filePath: string): string {
420  const SUFFIX_REG = /\.(?:d\.)?(ets|ts|mjs|cjs|js)$/;
421  return filePath.split(path.sep).join('/').replace(SUFFIX_REG, '');
422}
423
424export function getNormalizedOhmUrlByModuleRequest(moduleInfoByModuleRequest: Object, projectConfig: Object,
425  logger?: Object): string {
426  const normalizedPath = moduleInfoByModuleRequest.normalizedPath;
427  const pkgName = moduleInfoByModuleRequest.packageName;
428  const pkgInfo: Object = projectConfig.pkgContextInfo[pkgName];
429  if (!normalizedPath || !pkgName || !pkgInfo) {
430    const errInfo: LogData = LogDataFactory.newInstance(
431      ErrorCode.ETS2BUNDLE_INTERNAL_PACKAGE_NOT_FOUND_IN_CONTEXT_INFO,
432      ArkTSInternalErrorDescription,
433      `Failed to find package '${pkgName}'. ` +
434      `Failed to obtain package '${pkgName}' from the package context information.`
435    );
436    logger?.printError(errInfo);
437    return normalizedPath;
438  }
439  const isSo = pkgInfo.isSO ? 'Y' : 'N';
440  return `@normalized:${isSo}&${pkgInfo.moduleName}&${pkgInfo.bundleName}&${toUnixPath(normalizedPath)}&${pkgInfo.version}`;
441}
442
443export function getNormalizedOhmUrlByAliasName(aliasName: string, projectConfig: Object,
444  logger?: Object, filePath?: string): string {
445  let pkgName: string = aliasName;
446  const aliasPkgNameMap: Map<string, string> = projectConfig.dependencyAliasMap;
447  if (aliasPkgNameMap.has(aliasName)) {
448    pkgName = aliasPkgNameMap.get(aliasName);
449  }
450  const pkgInfo: Object = projectConfig.pkgContextInfo[pkgName];
451  if (!pkgInfo) {
452    const errInfo: LogData = LogDataFactory.newInstance(
453      ErrorCode.ETS2BUNDLE_INTERNAL_PACKAGE_NOT_FOUND_IN_CONTEXT_INFO,
454      ArkTSInternalErrorDescription,
455      `Failed to find package '${pkgName}'. ` +
456      `Failed to obtain package '${pkgName}' from the package context information.`
457    );
458    logger.printError(errInfo);
459  }
460  let normalizedPath: string = '';
461  if (!filePath) {
462    if (!pkgInfo.entryPath) {
463      const errInfo: LogData = LogDataFactory.newInstance(
464        ErrorCode.ETS2BUNDLE_INTERNAL_PACKAGE_ENTRY_FILE_NOT_FOUND,
465        ArkTSInternalErrorDescription,
466        `Failed to find entry file of '${pkgName}'. ` +
467        `Failed to obtain the entry file information of '${pkgName}' from the package context information.`
468      );
469      logger.printError(errInfo);
470    }
471    normalizedPath = `${pkgName}/${toUnixPath(pkgInfo.entryPath)}`;
472    normalizedPath = removeSuffix(normalizedPath);
473  } else {
474    const relativePath = toUnixPath(filePath).replace(aliasName, '');
475    normalizedPath = `${pkgName}${relativePath}`;
476  }
477  const isSo = pkgInfo.isSO ? 'Y' : 'N';
478  return `@normalized:${isSo}&${pkgInfo.moduleName}&${pkgInfo.bundleName}&${normalizedPath}&${pkgInfo.version}`;
479}
480
481export function getOhmUrlByByteCodeHar(moduleRequest: string, projectConfig: Object, rollupObject: Object, logger?: Object):
482  string | undefined {
483  if (projectConfig.useNormalizedOHMUrl &&
484    projectConfig.byteCodeHarInfo && Object.keys(projectConfig.byteCodeHarInfo).length > 0) {
485    const moduleInfoByModuleRequest: Object = rollupObject.share?.importResolver?.(moduleRequest);
486    if (moduleInfoByModuleRequest) {
487       return getNormalizedOhmUrlByModuleRequest(moduleInfoByModuleRequest, projectConfig, logger);
488    }
489    let aliasName: string = getAliasNameFromPackageMap(projectConfig.byteCodeHarInfo, moduleRequest);
490    if (aliasName) {
491      return getNormalizedOhmUrlByAliasName(aliasName, projectConfig, logger);
492    }
493    for (const byteCodeHarName in projectConfig.byteCodeHarInfo) {
494      if (moduleRequest.startsWith(byteCodeHarName + '/')) {
495        return getNormalizedOhmUrlByAliasName(byteCodeHarName, projectConfig, logger, moduleRequest);
496      }
497    }
498  }
499  return undefined;
500}
501
502function getAliasNameFromPackageMap(externalPkgMap: Object, moduleRequest: string): string | undefined {
503  // Matches strings that contain zero or more `/` or `\` characters
504  const ONLY_SLASHES_REGEX: RegExp = /^\/*$|^\\*$/;
505  const keys: string[] = Object.keys(externalPkgMap);
506  for (const key of keys) {
507    if (moduleRequest === key) {
508      return key;
509    }
510    // In the following cases, the moduleRequest matches the aliasName field in the packageMap
511    // case1: key = "hsp", moduleRequest = "hsp/"
512    // case2: key = "hsp", moduleRequest = "hsp\\"
513    if (moduleRequest.length > key.length && moduleRequest.startsWith(key)) {
514      const remaining: string = moduleRequest.replace(key, '');
515      if (ONLY_SLASHES_REGEX.test(remaining)) {
516        return key;
517      }
518    }
519  }
520  return undefined;
521}
522
523export function getOhmUrlByExternalPackage(moduleRequest: string, projectConfig: Object, rollupObject?: Object, logger?: Object,
524  useNormalizedOHMUrl: boolean = false): string | undefined {
525  // The externalPkgMap store the ohmurl with the alias of hsp package and the hars depended on bytecode har.
526  let externalPkgMap: Object = Object.assign({}, projectConfig.hspNameOhmMap, projectConfig.harNameOhmMap);
527  if (Object.keys(externalPkgMap).length !== 0) {
528    const moduleInfoByModuleRequest: Object = rollupObject?.share?.importResolver?.(moduleRequest);
529    if (useNormalizedOHMUrl && moduleInfoByModuleRequest) {
530       return getNormalizedOhmUrlByModuleRequest(moduleInfoByModuleRequest, projectConfig, logger);
531    }
532    let aliasName: string = getAliasNameFromPackageMap(externalPkgMap, moduleRequest);
533    if (aliasName) {
534      if (useNormalizedOHMUrl) {
535        return getNormalizedOhmUrlByAliasName(aliasName, projectConfig, logger);
536      }
537      // case1: "@ohos/lib" ---> "@bundle:bundleName/lib/ets/index"
538      return externalPkgMap[moduleRequest];
539    }
540
541    for (const externalPkgName in externalPkgMap) {
542      if (moduleRequest.startsWith(externalPkgName + '/')) {
543        if (useNormalizedOHMUrl) {
544          return getNormalizedOhmUrlByAliasName(externalPkgName, projectConfig, logger, moduleRequest);
545        }
546        // case2: "@ohos/lib/src/main/ets/pages/page1" ---> "@bundle:bundleName/lib/ets/pages/page1"
547        const idx: number = externalPkgMap[externalPkgName].split('/', 2).join('/').length;
548        const ohmName: string = externalPkgMap[externalPkgName].substring(0, idx);
549        if (moduleInfoByModuleRequest) {
550          // when useNormalizedOHMUrl=false, and packageName combines with directory to import file, OhmUrl is supposed to end with "index"
551          const relativePathByModuleRequest: string = toUnixPath(moduleInfoByModuleRequest.normalizedPath).split(SRC_MAIN)[1];
552          // "@bundle:bundleName/lib" + "/ets/pages/page1"
553          return ohmName + relativePathByModuleRequest;
554        }
555        if (moduleRequest.indexOf(externalPkgName + '/' + SRC_MAIN) === 0) {
556          return moduleRequest.replace(externalPkgName + '/' + SRC_MAIN, ohmName);
557        } else {
558          return moduleRequest.replace(externalPkgName, ohmName);
559        }
560      }
561    }
562  }
563  return undefined;
564}
565
566function replaceHarDependency(item: string, moduleRequest: string, projectConfig: Object): string {
567  const hspOhmUrl: string | undefined = getOhmUrlByExternalPackage(moduleRequest, projectConfig);
568  if (hspOhmUrl !== undefined) {
569    return item.replace(/(['"])(?:\S+)['"]/, (_, quotation) => {
570      return quotation + hspOhmUrl + quotation;
571    });
572  }
573  return item;
574}
575
576function locateActualFilePathWithModuleRequest(absolutePath: string): string {
577  if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isDirectory()) {
578    return absolutePath;
579  }
580
581  const filePath: string = absolutePath + getExtensionIfUnfullySpecifiedFilepath(absolutePath);
582  if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
583    return absolutePath;
584  }
585
586  return path.join(absolutePath, 'index');
587}
588
589function replaceRelativeDependency(item: string, moduleRequest: string, sourcePath: string, projectConfig: Object): string {
590  if (sourcePath && projectConfig.compileMode === ESMODULE) {
591    // remove file extension from moduleRequest
592    const SUFFIX_REG: RegExp = /\.(?:[cm]?js|[e]?ts|json)$/;
593    moduleRequest = moduleRequest.replace(SUFFIX_REG, '');
594
595    // normalize the moduleRequest
596    item = item.replace(/(['"])(?:\S+)['"]/, (_, quotation) => {
597      let normalizedModuleRequest: string = toUnixPath(path.normalize(moduleRequest));
598      if (moduleRequest.startsWith('./')) {
599        normalizedModuleRequest = './' + normalizedModuleRequest;
600      }
601      return quotation + normalizedModuleRequest + quotation;
602    });
603
604    const filePath: string =
605      locateActualFilePathWithModuleRequest(path.resolve(path.dirname(sourcePath), moduleRequest));
606    const result: RegExpMatchArray | null =
607      filePath.match(/(\S+)(\/|\\)src(\/|\\)(?:main|ohosTest)(\/|\\)(ets|js)(\/|\\)(\S+)/);
608    if (result && projectConfig.aceModuleJsonPath) {
609      const npmModuleIdx: number = result[1].search(/(\/|\\)node_modules(\/|\\)/);
610      const projectRootPath: string = projectConfig.projectRootPath;
611      if (npmModuleIdx === -1 || npmModuleIdx === projectRootPath.search(/(\/|\\)node_modules(\/|\\)/)) {
612        const packageInfo: string[] = getPackageInfo(projectConfig.aceModuleJsonPath);
613        const bundleName: string = packageInfo[0];
614        const moduleName: string = packageInfo[1];
615        moduleRequest = `@bundle:${bundleName}/${moduleName}/${result[5]}/${toUnixPath(result[7])}`;
616        item = item.replace(/(['"])(?:\S+)['"]/, (_, quotation) => {
617          return quotation + moduleRequest + quotation;
618        });
619      }
620    }
621  }
622  return item;
623}
624
625/**
626 * Informantion of build files
627 */
628export interface ModuleInfo {
629  content: string,
630  /**
631   * the path in build cache dir
632   */
633  buildFilePath: string,
634  /**
635   * the `originSourceFilePath` relative to project root dir.
636   */
637  relativeSourceFilePath: string,
638  /**
639   * the origin source file path will be set with rollup moduleId when obfuscate intermediate js source code,
640   * whereas be set with tsc node.fileName when obfuscate intermediate ts source code.
641   */
642  originSourceFilePath?: string,
643  rollupModuleId?: string
644}
645
646export async function writeObfuscatedSourceCode(moduleInfo: ModuleInfo, logger: Function | Object,
647  projectConfig: Object, rollupNewSourceMaps: Object = {}): Promise<void> {
648  /**
649   * Obfuscation mode rules:
650   * - When isArkguardEnabled=true (source code obfuscation): Obfuscate ALL files
651   * - When isBytecodeObfEnabled=true (bytecode obfuscation): Only decl files will be obfuscated
652   * - These two flags are mutually exclusive (only one can be true at a time)
653   */
654  let shouldSkipObfuscation = false;
655  if (projectConfig.isBytecodeObfEnabled) {
656    shouldSkipObfuscation = !/\.d\.e?ts$/.test(moduleInfo.buildFilePath);
657  }
658
659  const isObfuscatorEnabled = projectConfig.arkObfuscator &&
660    (projectConfig.isArkguardEnabled || !shouldSkipObfuscation);
661  if (compileToolIsRollUp() && isObfuscatorEnabled) {
662    startFilesEvent(moduleInfo.buildFilePath);
663    const recordInfo = MemoryMonitor.recordStage(MemoryDefine.WRITE_OBFUSCATED_SOURCE_CODE);
664    const previousStageSourceMap: sourceMap.RawSourceMap | undefined = getPreviousStageSourceMap(moduleInfo, rollupNewSourceMaps);
665    collectObfuscationFileContent(moduleInfo, projectConfig, previousStageSourceMap);
666    // We should skip obfuscate here if we need reObfuscate all files, since they will all be obfuscated later in function 'reObfuscate'.
667    if (!projectConfig.arkObfuscator.shouldReObfuscate) {
668      MemoryUtils.tryGC();
669      await writeArkguardObfuscatedSourceCode(moduleInfo, logger as Function, projectConfig, previousStageSourceMap);
670      MemoryUtils.tryGC();
671    }
672    MemoryMonitor.stopRecordStage(recordInfo);
673    endFilesEvent(moduleInfo.buildFilePath, undefined, true);
674    return;
675  }
676  mkdirsSync(path.dirname(moduleInfo.buildFilePath));
677  if (!compileToolIsRollUp()) {
678    await writeMinimizedSourceCode(moduleInfo.content, moduleInfo.buildFilePath, logger, projectConfig.compileHar);
679    return;
680  }
681
682  if (moduleInfo.originSourceFilePath) {
683    const originSourceFilePath = toUnixPath(moduleInfo.originSourceFilePath);
684    let genFileInHar: GeneratedFileInHar = harFilesRecord.get(originSourceFilePath);
685
686    if (!genFileInHar) {
687      genFileInHar = { sourcePath: originSourceFilePath };
688    }
689    if (!genFileInHar.sourceCachePath) {
690      genFileInHar.sourceCachePath = toUnixPath(moduleInfo.buildFilePath);
691    }
692    harFilesRecord.set(originSourceFilePath, genFileInHar);
693  }
694
695  writeFileSyncCaseAware(moduleInfo.buildFilePath, moduleInfo.content);
696}
697
698export function getPreviousStageSourceMap(moduleInfo: ModuleInfo, rollupNewSourceMaps: Object = {}): sourceMap.RawSourceMap | undefined {
699  const isDeclaration = (/\.d\.e?ts$/).test(moduleInfo.buildFilePath);
700  const sourceMapGeneratorInstance = SourceMapGenerator.getInstance();
701  let previousStageSourceMap: sourceMap.RawSourceMap | undefined = undefined;
702  if (moduleInfo.relativeSourceFilePath.length > 0 && !isDeclaration) {
703    const selectedFilePath = sourceMapGeneratorInstance.isNewSourceMaps() ? moduleInfo.rollupModuleId! : moduleInfo.relativeSourceFilePath;
704    previousStageSourceMap = sourceMapGeneratorInstance.getSpecifySourceMap(rollupNewSourceMaps, selectedFilePath) as sourceMap.RawSourceMap;
705  }
706  return previousStageSourceMap;
707}
708
709/**
710 * collect current obfuscated input content, used for re-obfuscate.
711 */
712export function collectObfuscationFileContent(moduleInfo: ModuleInfo, projectConfig: Object,
713  previousStageSourceMap: sourceMap.RawSourceMap | undefined): void {
714  const arkObfuscator = projectConfig.arkObfuscator;
715  const isDeclaration = (/\.d\.e?ts$/).test(moduleInfo.buildFilePath);
716  const isOriginalDeclaration = (/\.d\.e?ts$/).test(moduleInfo.originSourceFilePath);
717
718  // We only collect non-declaration files, unless it's a user-written declaration file
719  if (arkObfuscator.filePathManager && (!isDeclaration || isOriginalDeclaration)) {
720    const fileContent: ProjectCollections.FileContent = {
721      moduleInfo: moduleInfo,
722      previousStageSourceMap: previousStageSourceMap as ts.RawSourceMap,
723    };
724    arkObfuscator.fileContentManager.updateFileContent(fileContent);
725  }
726}
727
728/**
729 * This Api only be used by rollup compiling process & only be
730 * exported for unit test.
731 */
732export async function writeArkguardObfuscatedSourceCode(moduleInfo: ModuleInfo, printObfLogger: Function,
733  projectConfig: Object, previousStageSourceMap: sourceMap.RawSourceMap | undefined): Promise<void> {
734  const arkObfuscator = projectConfig.arkObfuscator;
735  const isDeclaration = (/\.d\.e?ts$/).test(moduleInfo.buildFilePath);
736  const packageDir = projectConfig.packageDir;
737  const projectRootPath = projectConfig.projectRootPath;
738  const useNormalized = projectConfig.useNormalizedOHMUrl;
739  const localPackageSet = projectConfig.localPackageSet;
740  const useTsHar = projectConfig.useTsHar;
741  const sourceMapGeneratorInstance = SourceMapGenerator.getInstance();
742
743  const historyNameCache: Map<string, string> = getNameCacheByPath(moduleInfo, isDeclaration, projectRootPath);
744
745  let mixedInfo: { content: string, sourceMap?: Object, nameCache?: Object, unobfuscationNameMap?: Map<string, Set<string>>};
746  let projectInfo: {
747    packageDir: string,
748    projectRootPath: string,
749    localPackageSet: Set<string>,
750    useNormalized: boolean,
751    useTsHar: boolean
752  } = { packageDir, projectRootPath, localPackageSet, useNormalized, useTsHar };
753  let filePathObj = { buildFilePath: moduleInfo.buildFilePath, relativeFilePath: moduleInfo.relativeSourceFilePath };
754
755  try {
756    startSingleFileEvent(EventList.OBFUSCATE, performancePrinter.timeSumPrinter, filePathObj.buildFilePath);
757    mixedInfo = await arkObfuscator.obfuscate(moduleInfo.content, filePathObj, previousStageSourceMap,
758      historyNameCache, moduleInfo.originSourceFilePath, projectInfo);
759    endSingleFileEvent(EventList.OBFUSCATE, performancePrinter.timeSumPrinter);
760  } catch (err) {
761    const errorInfo: string = `ArkTS:INTERNAL ERROR: Failed to obfuscate file '${moduleInfo.relativeSourceFilePath}' with arkguard. ${err}`;
762    const errorCodeInfo: HvigorErrorInfo = {
763      code: '10810001',
764      description: 'ArkTS compiler Error',
765      cause: `ArkTS:INTERNAL ERROR: Failed to obfuscate file '${moduleInfo.relativeSourceFilePath}' with arkguard. ${err}`,
766      position: moduleInfo.relativeSourceFilePath,
767      solutions: [`Please modify the code based on the error information.`],
768    };
769    printObfLogger(errorInfo, errorCodeInfo, 'error');
770  }
771
772  if (mixedInfo.sourceMap && !isDeclaration) {
773    const selectedFilePath = sourceMapGeneratorInstance.isNewSourceMaps() ? moduleInfo.rollupModuleId! : moduleInfo.relativeSourceFilePath;
774    mixedInfo.sourceMap.sources = [moduleInfo.relativeSourceFilePath];
775    sourceMapGeneratorInstance.fillSourceMapPackageInfo(moduleInfo.rollupModuleId!, mixedInfo.sourceMap);
776    sourceMapGeneratorInstance.updateSourceMap(selectedFilePath, mixedInfo.sourceMap);
777  }
778
779  setNewNameCache(mixedInfo.nameCache, isDeclaration, moduleInfo, projectConfig);
780
781  setUnobfuscationNames(mixedInfo.unobfuscationNameMap, moduleInfo.relativeSourceFilePath, isDeclaration);
782
783  const newFilePath: string = tryMangleFileName(moduleInfo.buildFilePath, projectConfig, moduleInfo.originSourceFilePath);
784  if (newFilePath !== moduleInfo.buildFilePath && !isDeclaration) {
785    sourceMapGeneratorInstance.saveKeyMappingForObfFileName(moduleInfo.rollupModuleId!);
786  }
787
788  writeObfuscatedFile(newFilePath, mixedInfo.content ?? '');
789}
790/**
791 * This function will be called when obfuscating sourceFile and declaration file.
792 * Declaration file is not affected by bytecode obfuscation.
793 * When bytecode obfuscation is enabel, the name of sourceFile will not be obfuscated.
794 * Because harFilesRecord is collected in this function
795 * This parameter is added to process sourceFile and return early only when bytecode obfuscation is turned on.
796 * It does not affect the original call effect.
797 */
798export function tryMangleFileName(filePath: string, projectConfig: Object, originalFilePath: string, skipObf: boolean = false): string {
799  originalFilePath = toUnixPath(originalFilePath);
800  let isOhModule = isPackageModulesFile(originalFilePath, projectConfig);
801  let genFileInHar: GeneratedFileInHar = harFilesRecord.get(originalFilePath);
802  if (!genFileInHar) {
803    genFileInHar = { sourcePath: originalFilePath };
804    harFilesRecord.set(originalFilePath, genFileInHar);
805  }
806  if (skipObf) {
807    return filePath;
808  }
809  if (projectConfig.obfuscationMergedObConfig?.options?.enableFileNameObfuscation && !isOhModule) {
810    const mangledFilePath: string = mangleFilePath(filePath);
811    if ((/\.d\.e?ts$/).test(filePath)) {
812      genFileInHar.obfuscatedDeclarationCachePath = mangledFilePath;
813    } else {
814      genFileInHar.obfuscatedSourceCachePath = mangledFilePath;
815    }
816    filePath = mangledFilePath;
817  } else if (!(/\.d\.e?ts$/).test(filePath)) {
818    genFileInHar.sourceCachePath = filePath;
819  }
820  return filePath;
821}
822
823export async function mangleDeclarationFileName(printObfLogger: Function, projectConfig: Object,
824  sourceFileBelongProject: Map<string, string>): Promise<void> {
825  for (const [sourcePath, genFilesInHar] of harFilesRecord) {
826    if (genFilesInHar.originalDeclarationCachePath && genFilesInHar.originalDeclarationContent) {
827      let filePath = genFilesInHar.originalDeclarationCachePath;
828      let relativeSourceFilePath = getRelativeSourcePath(filePath,
829         projectConfig.projectRootPath, sourceFileBelongProject.get(toUnixPath(filePath)));
830      await writeObfuscatedSourceCode({
831          content: genFilesInHar.originalDeclarationContent,
832          buildFilePath: genFilesInHar.originalDeclarationCachePath,
833          relativeSourceFilePath: relativeSourceFilePath,
834          originSourceFilePath: sourcePath
835        }, printObfLogger, projectConfig, {});
836    }
837  }
838}
839
840export async function writeMinimizedSourceCode(content: string, filePath: string, logger: Object,
841  isHar: boolean = false): Promise<void> {
842  let result: MinifyOutput;
843  try {
844    const minifyOptions = {
845      compress: {
846        join_vars: false,
847        sequences: 0,
848        directives: false
849      }
850    };
851    if (!isHar) {
852      minifyOptions.format = {
853        semicolons: false,
854        beautify: true,
855        indent_level: 2
856      };
857    }
858    result = await minify(content, minifyOptions);
859  } catch {
860    const errInfo: LogData = LogDataFactory.newInstance(
861      ErrorCode.ETS2BUNDLE_INTERNAL_SOURCE_CODE_OBFUSCATION_FAILED,
862      ArkTSInternalErrorDescription,
863      `Failed to obfuscate source code for ${filePath}`
864    );
865    logger.printError(errInfo);
866  }
867
868  fs.writeFileSync(filePath, result.code);
869}
870
871//Writes declaration files in debug mode
872export function writeDeclarationFiles(compileMode: string): void {
873  if (compileToolIsRollUp() && compileMode === ESMODULE) {
874    for (const genFilesInHar of harFilesRecord.values()) {
875      if (genFilesInHar.originalDeclarationCachePath && genFilesInHar.originalDeclarationContent) {
876        mkdirsSync(path.dirname(genFilesInHar.originalDeclarationCachePath));
877        writeFileSyncCaseAware(genFilesInHar.originalDeclarationCachePath, genFilesInHar.originalDeclarationContent);
878      }
879    }
880  }
881}
882
883export function genBuildPath(filePath: string, projectPath: string, buildPath: string, projectConfig: Object): string {
884  filePath = toUnixPath(filePath);
885  if (filePath.endsWith(EXTNAME_MJS)) {
886    filePath = filePath.replace(/\.mjs$/, EXTNAME_JS);
887  }
888  if (filePath.endsWith(EXTNAME_CJS)) {
889    filePath = filePath.replace(/\.cjs$/, EXTNAME_JS);
890  }
891  projectPath = toUnixPath(projectPath);
892
893  if (isPackageModulesFile(filePath, projectConfig)) {
894    const packageDir: string = projectConfig.packageDir;
895    const fakePkgModulesPath: string = toUnixPath(path.join(projectConfig.projectRootPath, packageDir));
896    let output: string = '';
897    if (filePath.indexOf(fakePkgModulesPath) === -1) {
898      const hapPath: string = toUnixPath(projectConfig.projectRootPath);
899      const tempFilePath: string = filePath.replace(hapPath, '');
900      const sufStr: string = tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1);
901      output = path.join(projectConfig.nodeModulesPath, ZERO, sufStr);
902    } else {
903      output = filePath.replace(fakePkgModulesPath, path.join(projectConfig.nodeModulesPath, ONE));
904    }
905    return output;
906  }
907
908  if (filePath.indexOf(projectPath) !== -1) {
909    const sufStr: string = filePath.replace(projectPath, '');
910    const output: string = path.join(buildPath, sufStr);
911    return output;
912  }
913
914  return '';
915}
916
917export function getPackageInfo(configFile: string): Array<string> {
918  if (packageCollection.has(configFile)) {
919    return packageCollection.get(configFile);
920  }
921  const data: Object = JSON.parse(fs.readFileSync(configFile).toString());
922  const bundleName: string = data.app.bundleName;
923  const moduleName: string = data.module.name;
924  packageCollection.set(configFile, [bundleName, moduleName]);
925  return [bundleName, moduleName];
926}
927
928/**
929 * This Api only used by webpack compiling process - result_process
930 * @param sourcePath The path in build cache dir
931 * @param sourceContent The intermediate js source code
932 */
933export function generateSourceFilesToTemporary(sourcePath: string, sourceContent: string, sourceMap: Object,
934  projectConfig: Object, logger: Object): void {
935    let jsFilePath: string = genTemporaryPath(sourcePath, projectConfig.projectPath, process.env.cachePath,
936      projectConfig, undefined);
937  if (jsFilePath.length === 0) {
938    return;
939  }
940  if (jsFilePath.endsWith(EXTNAME_ETS)) {
941    jsFilePath = jsFilePath.replace(/\.ets$/, EXTNAME_JS);
942  } else {
943    jsFilePath = jsFilePath.replace(/\.ts$/, EXTNAME_JS);
944  }
945  let sourceMapFile: string = genSourceMapFileName(jsFilePath);
946  if (sourceMapFile.length > 0 && projectConfig.buildArkMode === 'debug') {
947    let source = toUnixPath(sourcePath).replace(toUnixPath(projectConfig.projectRootPath) + '/', '');
948    // adjust sourceMap info
949    sourceMap.sources = [source];
950    sourceMap.file = path.basename(sourceMap.file);
951    delete sourceMap.sourcesContent;
952    newSourceMaps[source] = sourceMap;
953  }
954  sourceContent = transformModuleSpecifier(sourcePath, sourceContent, projectConfig);
955
956  mkdirsSync(path.dirname(jsFilePath));
957  if (projectConfig.buildArkMode === 'debug') {
958    fs.writeFileSync(jsFilePath, sourceContent);
959    return;
960  }
961
962  writeObfuscatedSourceCode({content: sourceContent, buildFilePath: jsFilePath, relativeSourceFilePath: ''},
963    logger, projectConfig);
964}
965
966export function genAbcFileName(temporaryFile: string): string {
967  let abcFile: string = temporaryFile;
968  if (temporaryFile.endsWith(EXTNAME_TS)) {
969    abcFile = temporaryFile.replace(/\.ts$/, EXTNAME_ABC);
970  } else {
971    abcFile = temporaryFile.replace(/\.js$/, EXTNAME_ABC);
972  }
973  return abcFile;
974}
975
976export function isOhModules(projectConfig: Object): boolean {
977  return projectConfig.packageDir === OH_MODULES;
978}
979
980export function isEs2Abc(projectConfig: Object): boolean {
981  return projectConfig.pandaMode === ES2ABC || projectConfig.pandaMode === 'undefined' ||
982    projectConfig.pandaMode === undefined;
983}
984
985export function isTs2Abc(projectConfig: Object): boolean {
986  return projectConfig.pandaMode === TS2ABC;
987}
988
989export function genProtoFileName(temporaryFile: string): string {
990  return temporaryFile.replace(/\.(?:[tj]s|json)$/, EXTNAME_PROTO_BIN);
991}
992
993export function genMergeProtoFileName(temporaryFile: string): string {
994  let protoTempPathArr: string[] = temporaryFile.split(TEMPORARY);
995  const sufStr: string = protoTempPathArr[protoTempPathArr.length - 1];
996  let protoBuildPath: string = path.join(process.env.cachePath, 'protos', sufStr);
997
998  return protoBuildPath;
999}
1000
1001export function removeDuplicateInfo(moduleInfos: Array<any>): Array<any> {
1002  const tempModuleInfos: any[] = Array<any>();
1003  moduleInfos.forEach((item) => {
1004    let check: boolean = tempModuleInfos.every((newItem) => {
1005      return item.tempFilePath !== newItem.tempFilePath;
1006    });
1007    if (check) {
1008      tempModuleInfos.push(item);
1009    }
1010  });
1011  moduleInfos = tempModuleInfos;
1012
1013  return moduleInfos;
1014}
1015
1016export function buildCachePath(tailName: string, projectConfig: Object, logger: Object): string {
1017  let pathName: string = process.env.cachePath !== undefined ?
1018    path.join(projectConfig.cachePath, tailName) : path.join(projectConfig.aceModuleBuild, tailName);
1019  validateFilePathLength(pathName, logger);
1020  return pathName;
1021}
1022
1023export function getArkBuildDir(arkDir: string): string {
1024  if (isWindows()) {
1025    return path.join(arkDir, 'build-win');
1026  } else if (isMac()) {
1027    return path.join(arkDir, 'build-mac');
1028  } else {
1029    return path.join(arkDir, 'build');
1030  }
1031}
1032
1033export function getBuildBinDir(arkDir: string): string {
1034  return path.join(getArkBuildDir(arkDir), 'bin');
1035}
1036
1037export function cleanUpUtilsObjects(): void {
1038  newSourceMaps = {};
1039  nameCacheMap.clear();
1040  packageCollection.clear();
1041}
1042
1043export function compileToolIsRollUp(): boolean {
1044  return process.env.compileTool === 'rollup';
1045}
1046
1047export function transformOhmurlToRecordName(ohmurl: string): string {
1048  // @normalized:N&<moduleName>&<bunldName>&<packageName>/entry/ets/xxx/yyy&<version>
1049  // ----> <bunldName>&<packageName>/entry/ets/xxx/yyy&<version>
1050  return ohmurl.split(SEPARATOR_BITWISE_AND).slice(2).join(SEPARATOR_BITWISE_AND);
1051}
1052
1053export function transformOhmurlToPkgName(ohmurl: string): string {
1054  let normalizedPath: string = ohmurl.split(SEPARATOR_BITWISE_AND)[3];
1055  let paths: Array<string> = normalizedPath.split(SEPARATOR_SLASH);
1056  if (normalizedPath.startsWith(SEPARATOR_AT)) {
1057    // Spec: If the normalized import path starts with '@', the package name is before the second '/' in the normalized
1058    // import path, like:  @aaa/bbb/ccc/ddd ---> package name is @aaa/bbb
1059    return paths.slice(0, 2).join(SEPARATOR_SLASH);
1060  }
1061  return paths[0];
1062}
1063
1064export function writeFileSyncCaseAware(filePath: fs.PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, options?: fs.WriteFileOptions): void {
1065    // If filePath is not a file descriptor number (i.e., it's a file path) and the file at that path already exists,
1066    // delete the existing file first to ensure the written file name matches the input.
1067    if (typeof filePath !== 'number' && fs.existsSync(filePath)) {
1068      fs.unlinkSync(filePath);
1069    }
1070    fs.writeFileSync(filePath, data, options);
1071}