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}