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