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 childProcess from 'child_process'; 17import fs from 'fs'; 18import path from 'path'; 19import cluster from 'cluster'; 20 21import { 22 COMMONJS, 23 ESM, 24 ESMODULE, 25 EXTNAME_CJS, 26 EXTNAME_ETS, 27 EXTNAME_JS, 28 EXTNAME_JSON, 29 EXTNAME_MJS, 30 EXTNAME_PROTO_BIN, 31 EXTNAME_TS, 32 EXTNAME_TXT, 33 FAIL, 34 FILESINFO, 35 FILESINFO_TXT, 36 MAX_WORKER_NUMBER, 37 MODULES_ABC, 38 MODULES_CACHE, 39 NPM_ENTRIES_PROTO_BIN, 40 NPMENTRIES_TXT, 41 OH_MODULES, 42 PACKAGES, 43 PROTO_FILESINFO_TXT, 44 PROTOS, 45 red, 46 reset, 47 SOURCEMAPS, 48 SOURCEMAPS_JSON, 49 WIDGETS_ABC, 50 TS2ABC, 51 ES2ABC 52} from '../common/ark_define'; 53import { 54 needAotCompiler, 55 isMasterOrPrimary, 56 isAotMode 57} from '../utils'; 58import { CommonMode } from '../common/common_mode'; 59import { newSourceMaps } from '../transform'; 60import { 61 changeFileExtension, 62 getEs2abcFileThreadNumber, 63 isCommonJsPluginVirtualFile, 64 isCurrentProjectFiles, 65 shouldETSOrTSFileTransformToJS 66} from '../utils'; 67import { 68 isPackageModulesFile, 69 mkdirsSync, 70 toUnixPath, 71 toHashData, 72 validateFilePathLength 73} from '../../../utils'; 74import { 75 getPackageInfo, 76 getOhmUrlByFilepath, 77 isTs2Abc, 78 isEs2Abc 79} from '../../../ark_utils'; 80import { 81 generateAot, 82 generateBuiltinAbc, 83 FaultHandler 84} from '../../../gen_aot'; 85 86export class ModuleInfo { 87 filePath: string; 88 cacheFilePath: string; 89 recordName: string; 90 isCommonJs: boolean; 91 sourceFile: string; 92 packageName: string; 93 94 constructor(filePath: string, cacheFilePath: string, isCommonJs: boolean, recordName: string, sourceFile: string, 95 packageName: string 96 ) { 97 this.filePath = filePath; 98 this.cacheFilePath = cacheFilePath; 99 this.recordName = recordName; 100 this.isCommonJs = isCommonJs; 101 this.sourceFile = sourceFile; 102 this.packageName = packageName; 103 } 104} 105 106export class PackageEntryInfo { 107 pkgEntryPath: string; 108 pkgBuildPath: string; 109 constructor(pkgEntryPath: string, pkgBuildPath: string) { 110 this.pkgEntryPath = pkgEntryPath; 111 this.pkgBuildPath = pkgBuildPath; 112 } 113} 114 115export class ModuleMode extends CommonMode { 116 moduleInfos: Map<String, ModuleInfo>; 117 pkgEntryInfos: Map<String, PackageEntryInfo>; 118 hashJsonObject: any; 119 cacheSourceMapObject: any; 120 filesInfoPath: string; 121 npmEntriesInfoPath: string; 122 moduleAbcPath: string; 123 sourceMapPath: string; 124 cacheFilePath: string; 125 cacheSourceMapPath: string; 126 workerNumber: number; 127 npmEntriesProtoFilePath: string; 128 protoFilePath: string; 129 filterModuleInfos: Map<String, ModuleInfo>; 130 symlinkMap: any; 131 132 constructor(rollupObject: any) { 133 super(rollupObject); 134 this.moduleInfos = new Map<String, ModuleInfo>(); 135 this.pkgEntryInfos = new Map<String, PackageEntryInfo>(); 136 this.hashJsonObject = {}; 137 this.cacheSourceMapObject = {}; 138 this.filesInfoPath = path.join(this.projectConfig.cachePath, FILESINFO_TXT); 139 this.npmEntriesInfoPath = path.join(this.projectConfig.cachePath, NPMENTRIES_TXT); 140 const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC; 141 this.moduleAbcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC); 142 this.sourceMapPath = this.arkConfig.isDebug ? path.join(this.projectConfig.aceModuleBuild, SOURCEMAPS) : 143 path.join(this.projectConfig.cachePath, SOURCEMAPS); 144 this.cacheFilePath = path.join(this.projectConfig.cachePath, MODULES_CACHE); 145 this.cacheSourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON); 146 this.workerNumber = MAX_WORKER_NUMBER; 147 this.npmEntriesProtoFilePath = path.join(this.projectConfig.cachePath, PROTOS, NPM_ENTRIES_PROTO_BIN); 148 this.protoFilePath = path.join(this.projectConfig.cachePath, PROTOS, PROTO_FILESINFO_TXT); 149 this.hashJsonObject = {}; 150 this.filterModuleInfos = new Map<String, ModuleInfo>(); 151 this.symlinkMap = rollupObject.share.symlinkMap; 152 } 153 154 prepareForCompilation(rollupObject: any): void { 155 this.collectModuleFileList(rollupObject, rollupObject.getModuleIds()); 156 this.removeCacheInfo(rollupObject); 157 } 158 159 collectModuleFileList(module: any, fileList: IterableIterator<string>) { 160 let moduleInfos: Map<String, ModuleInfo> = new Map<String, ModuleInfo>(); 161 let pkgEntryInfos: Map<String, PackageEntryInfo> = new Map<String, PackageEntryInfo>(); 162 for (const moduleId of fileList) { 163 if (isCommonJsPluginVirtualFile(moduleId) || !isCurrentProjectFiles(moduleId, this.projectConfig)) { 164 continue; 165 } 166 const moduleInfo: any = module.getModuleInfo(moduleId); 167 if (moduleInfo['meta']['isNodeEntryFile']) { 168 this.getPackageEntryInfo(moduleId, moduleInfo['meta'], pkgEntryInfos); 169 } 170 171 this.processModuleInfos(moduleId, moduleInfos, moduleInfo['meta']); 172 } 173 this.moduleInfos = moduleInfos; 174 this.pkgEntryInfos = pkgEntryInfos; 175 } 176 177 private getPackageEntryInfo(filePath: string, metaInfo: any, pkgEntryInfos: Map<String, PackageEntryInfo>) { 178 if (metaInfo['isLocalDependency']) { 179 const hostModulesInfo: any = metaInfo['hostModulesInfo']; 180 const pkgBuildPath: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, metaInfo['moduleName']); 181 hostModulesInfo.forEach(hostModuleInfo => { 182 const hostDependencyName: string = hostModuleInfo['hostDependencyName']; 183 const hostModuleName: string = hostModuleInfo['hostModuleName']; 184 const pkgEntryPath: string = toUnixPath(path.join(`${PACKAGES}@${hostModuleName}`, hostDependencyName)); 185 if (!pkgEntryInfos.has(pkgEntryPath)) { 186 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 187 } 188 }); 189 return; 190 } 191 192 if (!metaInfo['pkgPath']) { 193 this.logger.debug("Failed to get 'pkgPath' from metaInfo. File: ", filePath); 194 return; 195 } 196 const pkgPath: string = metaInfo['pkgPath']; 197 let originPkgEntryPath: string = toUnixPath(filePath.replace(pkgPath, '')); 198 if (originPkgEntryPath.startsWith('/')) { 199 originPkgEntryPath = originPkgEntryPath.slice(1, originPkgEntryPath.length); 200 } 201 const pkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(pkgPath)); 202 let pkgBuildPath: string = path.join(pkgEntryPath, originPkgEntryPath); 203 pkgBuildPath = toUnixPath(pkgBuildPath.substring(0, pkgBuildPath.lastIndexOf('.'))); 204 if (!pkgEntryInfos.has(pkgEntryPath)) { 205 pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath)); 206 } 207 // create symlink path to actual path mapping in ohpm 208 if (this.projectConfig.packageDir == OH_MODULES && this.symlinkMap) { 209 const symlinkEntries: any = Object.entries(this.symlinkMap); 210 for (const [actualPath, symlinkPaths] of symlinkEntries) { 211 if (actualPath === pkgPath) { 212 (<string[]>symlinkPaths).forEach((symlink: string) => { 213 const symlinkPkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(symlink)); 214 if (!pkgEntryInfos.has(symlinkPkgEntryPath)) { 215 pkgEntryInfos.set(symlinkPkgEntryPath, new PackageEntryInfo(symlinkPkgEntryPath, pkgEntryPath)); 216 } 217 }); 218 break; 219 } 220 } 221 } 222 } 223 224 private processModuleInfos(moduleId: string, moduleInfos: Map<String, ModuleInfo>, metaInfo?: any) { 225 switch (path.extname(moduleId)) { 226 case EXTNAME_ETS: { 227 const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig) ? EXTNAME_JS : EXTNAME_TS; 228 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos); 229 break; 230 } 231 case EXTNAME_TS: { 232 const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig) ? EXTNAME_JS : ''; 233 this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos); 234 break; 235 } 236 case EXTNAME_JS: 237 case EXTNAME_MJS: 238 case EXTNAME_CJS: { 239 const extName: string = (moduleId.endsWith(EXTNAME_MJS) || moduleId.endsWith(EXTNAME_CJS)) ? EXTNAME_JS : ''; 240 const isCommonJS: boolean = metaInfo && metaInfo['commonjs'] && metaInfo['commonjs']['isCommonJS']; 241 this.addModuleInfoItem(moduleId, isCommonJS, extName, metaInfo, moduleInfos); 242 break; 243 } 244 case EXTNAME_JSON: { 245 this.addModuleInfoItem(moduleId, false, '', metaInfo, moduleInfos); 246 break; 247 } 248 default: 249 break; 250 } 251 } 252 253 private addModuleInfoItem(filePath: string, isCommonJs: boolean, extName: string, metaInfo: any, moduleInfos: any) { 254 let namespace: string = metaInfo['moduleName']; 255 let recordName: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, namespace); 256 let sourceFile: string = filePath.replace(this.projectConfig.projectRootPath + path.sep, ''); 257 let cacheFilePath: string = 258 this.genFileCachePath(filePath, this.projectConfig.projectRootPath, this.projectConfig.cachePath); 259 let packageName: string = ''; 260 if (isPackageModulesFile(filePath, this.projectConfig)) { 261 packageName = this.getPkgModulesFilePkgName(metaInfo['pkgPath']); 262 } else { 263 packageName = 264 metaInfo['isLocalDependency'] ? namespace : getPackageInfo(this.projectConfig.aceModuleJsonPath)[1]; 265 } 266 267 if (extName.length !== 0) { 268 cacheFilePath = changeFileExtension(cacheFilePath, extName); 269 } 270 271 cacheFilePath = toUnixPath(cacheFilePath); 272 recordName = toUnixPath(recordName); 273 sourceFile = toUnixPath(sourceFile); 274 packageName = toUnixPath(packageName); 275 276 moduleInfos.set(filePath, new ModuleInfo(filePath, cacheFilePath, isCommonJs, recordName, sourceFile, packageName)); 277 } 278 279 updateCachedSourceMaps(): void { 280 if (!fs.existsSync(this.cacheSourceMapPath)) { 281 this.cacheSourceMapObject = newSourceMaps; 282 return; 283 } 284 285 this.cacheSourceMapObject = JSON.parse(fs.readFileSync(this.cacheSourceMapPath).toString()); 286 287 // remove unused source files's sourceMap 288 let unusedFiles = []; 289 let compileFileList: Set<string> = new Set(); 290 this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleId: string) => { 291 compileFileList.add(toUnixPath(moduleId)); 292 }) 293 294 Object.keys(this.cacheSourceMapObject).forEach(key => { 295 const sourceFileAbsolutePath: string = toUnixPath(path.join(this.projectConfig.projectRootPath, key)); 296 if (!compileFileList.has(sourceFileAbsolutePath)) { 297 unusedFiles.push(key); 298 } 299 }); 300 unusedFiles.forEach(file => { 301 delete this.cacheSourceMapObject[file]; 302 }) 303 304 // update sourceMap 305 Object.keys(newSourceMaps).forEach(key => { 306 this.cacheSourceMapObject[key] = newSourceMaps[key]; 307 }); 308 } 309 310 buildModuleSourceMapInfo() { 311 if (this.projectConfig.widgetCompile) { 312 return; 313 } 314 315 this.updateCachedSourceMaps(); 316 this.triggerAsync(() => { 317 fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => { 318 if (err) { 319 this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps'); 320 } 321 fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath); 322 this.triggerEndSignal(); 323 }); 324 }); 325 } 326 327 generateEs2AbcCmd() { 328 const fileThreads = getEs2abcFileThreadNumber(); 329 this.cmdArgs.push(`"@${this.filesInfoPath}"`); 330 this.cmdArgs.push('--npm-module-entry-list'); 331 this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`); 332 this.cmdArgs.push('--output'); 333 this.cmdArgs.push(`"${this.moduleAbcPath}"`); 334 this.cmdArgs.push('--file-threads'); 335 this.cmdArgs.push(`"${fileThreads}"`); 336 this.cmdArgs.push('--merge-abc'); 337 if (isAotMode(this.projectConfig) && isEs2Abc(this.projectConfig)) { 338 this.cmdArgs.push("--type-extractor"); 339 } 340 } 341 342 addCacheFileArgs() { 343 this.cmdArgs.push('--cache-file'); 344 this.cmdArgs.push(`"@${this.cacheFilePath}"`); 345 } 346 347 private generateCompileFilesInfo() { 348 let filesInfo: string = ''; 349 this.moduleInfos.forEach((info) => { 350 const moduleType: string = info.isCommonJs ? COMMONJS : ESM; 351 filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`; 352 }); 353 fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8'); 354 } 355 356 private generateNpmEntriesInfo() { 357 let entriesInfo: string = ''; 358 for (const value of this.pkgEntryInfos.values()) { 359 entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`; 360 } 361 fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8'); 362 } 363 364 private generateAbcCacheFilesInfo(): void { 365 let abcCacheFilesInfo: string = ''; 366 367 // generate source file cache 368 this.moduleInfos.forEach((info) => { 369 let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN); 370 abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`; 371 }); 372 373 // generate npm entries cache 374 let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN); 375 abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`; 376 377 fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8'); 378 } 379 380 private genDescriptionsForMergedEs2abc() { 381 this.generateCompileFilesInfo(); 382 this.generateNpmEntriesInfo(); 383 this.generateAbcCacheFilesInfo(); 384 } 385 386 generateMergedAbcOfEs2Abc() { 387 // collect data error from subprocess 388 let errMsg: string = ''; 389 this.genDescriptionsForMergedEs2abc(); 390 const genAbcCmd: string = this.cmdArgs.join(' '); 391 try { 392 const child = this.triggerAsync(() => { 393 return childProcess.exec(genAbcCmd, { windowsHide: true }); 394 }); 395 child.on('exit', (code: any) => { 396 if (code === FAIL) { 397 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc'); 398 } 399 this.triggerEndSignal(); 400 this.processAotIfNeeded(); 401 }); 402 403 child.on('error', (err: any) => { 404 this.throwArkTsCompilerError(err.toString()); 405 }); 406 407 child.stderr.on('data', (data: any) => { 408 errMsg += data.toString(); 409 }); 410 411 child.stderr.on('end', (data: any) => { 412 if (errMsg !== undefined && errMsg.length > 0) { 413 this.logger.error(red, errMsg, reset); 414 } 415 }); 416 } catch (e) { 417 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString()); 418 } 419 } 420 421 filterModulesByHashJson() { 422 if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) { 423 for (const key of this.moduleInfos.keys()) { 424 this.filterModuleInfos.set(key, this.moduleInfos.get(key)); 425 } 426 return; 427 } 428 429 let updatedJsonObject: any = {}; 430 let jsonObject: any = {}; 431 let jsonFile: string = ''; 432 433 if (fs.existsSync(this.hashJsonFilePath)) { 434 jsonFile = fs.readFileSync(this.hashJsonFilePath).toString(); 435 jsonObject = JSON.parse(jsonFile); 436 this.filterModuleInfos = new Map<string, ModuleInfo>(); 437 for (const [key, value] of this.moduleInfos) { 438 const cacheFilePath: string = value.cacheFilePath; 439 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 440 if (!fs.existsSync(cacheFilePath)) { 441 this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`); 442 } 443 if (fs.existsSync(cacheProtoFilePath)) { 444 const hashCacheFileContentData: any = toHashData(cacheFilePath); 445 const hashProtoFileContentData: any = toHashData(cacheProtoFilePath); 446 if (jsonObject[cacheFilePath] === hashCacheFileContentData && 447 jsonObject[cacheProtoFilePath] === hashProtoFileContentData) { 448 updatedJsonObject[cacheFilePath] = cacheFilePath; 449 updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath; 450 continue; 451 } 452 } 453 this.filterModuleInfos.set(key, value); 454 } 455 } 456 457 this.hashJsonObject = updatedJsonObject; 458 } 459 460 getSplittedModulesByNumber() { 461 const result: any = []; 462 if (this.filterModuleInfos.size < this.workerNumber) { 463 for (const value of this.filterModuleInfos.values()) { 464 result.push([value]); 465 } 466 return result; 467 } 468 469 for (let i = 0; i < this.workerNumber; ++i) { 470 result.push([]); 471 } 472 473 let pos: number = 0; 474 for (const value of this.filterModuleInfos.values()) { 475 const chunk = pos % this.workerNumber; 476 result[chunk].push(value); 477 pos++; 478 } 479 480 return result; 481 } 482 483 invokeTs2AbcWorkersToGenProto(splittedModules) { 484 let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0); 485 ts2abcCmdArgs.push('--output-proto'); 486 ts2abcCmdArgs.push('--merge-abc'); 487 ts2abcCmdArgs.push('--input-file'); 488 if (isMasterOrPrimary()) { 489 this.setupCluster(cluster); 490 this.workerNumber = splittedModules.length; 491 for (let i = 0; i < this.workerNumber; ++i) { 492 const sn: number = i + 1; 493 const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`; 494 const workerData: any = { 495 inputs: JSON.stringify(splittedModules[i]), 496 cmd: ts2abcCmdArgs.join(' '), 497 workerFileName: workerFileName, 498 mode: ESMODULE, 499 cachePath: this.projectConfig.cachePath 500 }; 501 this.triggerAsync(() => { 502 const worker: any = cluster.fork(workerData); 503 worker.on('message', (errorMsg) => { 504 this.logger.error(red, errorMsg.data.toString(), reset); 505 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 506 }); 507 }); 508 } 509 } 510 } 511 512 processTs2abcWorkersToGenAbc() { 513 this.generateNpmEntriesInfo(); 514 let workerCount: number = 0; 515 if (isMasterOrPrimary()) { 516 cluster.on('exit', (worker, code, signal) => { 517 if (code === FAIL) { 518 this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc'); 519 } 520 workerCount++; 521 if (workerCount === this.workerNumber) { 522 this.generateNpmEntryToGenProto(); 523 this.generateProtoFilesInfo(); 524 this.mergeProtoToAbc(); 525 this.processAotIfNeeded(); 526 this.afterCompilationProcess(); 527 } 528 this.triggerEndSignal(); 529 }); 530 if (this.workerNumber == 0) { 531 // process aot for no source file changed. 532 this.processAotIfNeeded(); 533 } 534 } 535 } 536 537 private processAotIfNeeded(): void { 538 if (!needAotCompiler(this.projectConfig)) { 539 return; 540 } 541 let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); }) 542 const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.initCmdEnv(), 543 this.projectConfig.cachePath, this.logger, faultHandler, this.projectConfig.pandaMode); 544 generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.moduleAbcPath, this.projectConfig, this.logger, faultHandler); 545 } 546 547 private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string { 548 const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), ''); 549 const output: string = path.join(cachePath, sufStr); 550 return output; 551 } 552 553 private getPkgModulesFilePkgName(pkgPath: string) { 554 pkgPath = toUnixPath(pkgPath); 555 const packageDir: string = this.projectConfig.packageDir; 556 const projectRootPath = toUnixPath(this.projectConfig.projectRootPath); 557 const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir)); 558 let pkgName: string = ''; 559 if (pkgPath.includes(projectPkgModulesPath)) { 560 pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, '')); 561 } else { 562 for (const key in this.projectConfig.modulePathMap) { 563 const value: string = this.projectConfig.modulePathMap[key]; 564 const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir)); 565 if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) { 566 const tempFilePath: string = pkgPath.replace(projectRootPath, ''); 567 pkgName = path.join(`${PACKAGES}@${key}`, 568 tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1)); 569 break; 570 } 571 } 572 } 573 574 return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES); 575 } 576 577 private generateProtoFilesInfo() { 578 validateFilePathLength(this.protoFilePath, this.logger); 579 mkdirsSync(path.dirname(this.protoFilePath)); 580 let protoFilesInfo: string = ''; 581 const sortModuleInfos: any = new Map([...this.moduleInfos].sort()); 582 for (const value of sortModuleInfos.values()) { 583 const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN); 584 protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`; 585 } 586 if (this.pkgEntryInfos.size > 0) { 587 protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`; 588 } 589 fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8'); 590 } 591 592 private mergeProtoToAbc() { 593 mkdirsSync(this.projectConfig.aceModuleBuild); 594 const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${ 595 this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`; 596 try { 597 childProcess.execSync(cmd, { windowsHide: true }); 598 } catch (e) { 599 this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString()); 600 } 601 } 602 603 private afterCompilationProcess() { 604 this.writeHashJson(); 605 } 606 607 private writeHashJson() { 608 if (this.hashJsonFilePath.length === 0) { 609 return; 610 } 611 612 for (const value of this.filterModuleInfos.values()) { 613 const cacheFilePath: string = value.cacheFilePath; 614 const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN); 615 if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) { 616 this.throwArkTsCompilerError( 617 `ArkTS:ERROR ${cacheFilePath} or ${cacheProtoFilePath} is lost` 618 ); 619 } 620 const hashCacheFileContentData: any = toHashData(cacheFilePath); 621 const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath); 622 this.hashJsonObject[cacheFilePath] = hashCacheFileContentData; 623 this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData; 624 } 625 626 fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject)); 627 } 628 629 private generateNpmEntryToGenProto() { 630 if (this.pkgEntryInfos.size <= 0) { 631 return; 632 } 633 mkdirsSync(path.dirname(this.npmEntriesProtoFilePath)); 634 const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${ 635 this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`; 636 try { 637 childProcess.execSync(cmd, { windowsHide: true }); 638 } catch (e) { 639 this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString()); 640 } 641 } 642 643 private removeCompilationCache(): void { 644 if (isEs2Abc(this.projectConfig)) { 645 this.removeEs2abcCompilationCache(); 646 } else if (isTs2Abc(this.projectConfig)) { 647 this.removeTs2abcCompilationCache(); 648 } else { 649 this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either 650 "${TS2ABC}" or "${ES2ABC}"`); 651 } 652 } 653 654 private removeEs2abcCompilationCache(): void { 655 if (fs.existsSync(this.cacheFilePath)) { 656 const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8'); 657 const lines: any = data.split(/\r?\n/); 658 lines.forEach(line => { 659 const [, abcCacheFilePath]: any = line.split(';'); 660 if (fs.existsSync(abcCacheFilePath)) { 661 fs.unlinkSync(abcCacheFilePath); 662 } 663 }); 664 fs.unlinkSync(this.cacheFilePath); 665 } 666 } 667 668 private removeTs2abcCompilationCache(): void { 669 if (fs.existsSync(this.hashJsonFilePath)) { 670 fs.unlinkSync(this.hashJsonFilePath); 671 } 672 if (fs.existsSync(this.protoFilePath)) { 673 const data: string = fs.readFileSync(this.protoFilePath, 'utf-8'); 674 const lines: any = data.split(/\r?\n/); 675 lines.forEach(line => { 676 const protoFilePath: string = line; 677 if (fs.existsSync(protoFilePath)) { 678 fs.unlinkSync(protoFilePath); 679 } 680 }); 681 fs.unlinkSync(this.protoFilePath); 682 } 683 } 684} 685