1/* 2 * Copyright (c) 2021 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 * as fs from 'fs'; 17import * as path from 'path'; 18import cluster from 'cluster'; 19import process from 'process'; 20import os from 'os'; 21import events from 'events'; 22import Compiler from 'webpack/lib/Compiler'; 23import { logger } from './compile_info'; 24import * as childProcess from 'child_process'; 25import { 26 toUnixPath, 27 toHashData, 28 mkdirsSync, 29 nodeLargeOrEqualTargetVersion, 30 removeDir, 31 validateFilePathLength, 32 unlinkSync, 33 isPackageModulesFile, 34 genTemporaryPath 35} from './utils'; 36import { 37 buildCachePath, 38 genAbcFileName, 39 genBuildPath, 40 genMergeProtoFileName, 41 genProtoFileName, 42 getOhmUrlByFilepath, 43 getPackageInfo, 44 isEs2Abc, 45 isTs2Abc, 46 newSourceMaps, 47 removeDuplicateInfo 48} from './ark_utils'; 49import { projectConfig } from '../main'; 50import { 51 ESMODULE, 52 JSBUNDLE, 53 NODE_MODULES, 54 ES2ABC, 55 EXTNAME_D_ETS, 56 EXTNAME_D_TS, 57 EXTNAME_ETS, 58 EXTNAME_JS, 59 EXTNAME_TS, 60 EXTNAME_MJS, 61 EXTNAME_CJS, 62 EXTNAME_JSON, 63 EXTNAME_JS_MAP, 64 FAIL, 65 MODULELIST_JSON, 66 MODULES_ABC, 67 PREBUILDINFO_JSON, 68 SUCCESS, 69 SOURCEMAPS_JSON, 70 SOURCEMAPS, 71 TEMPORARY, 72 TS2ABC, 73 PROTO_FILESINFO_TXT, 74 NPMENTRIES_TXT, 75 EXTNAME_PROTO_BIN, 76 FILESINFO_TXT, 77 MANAGE_WORKERS_SCRIPT, 78 MAX_WORKER_NUMBER, 79 GEN_ABC_SCRIPT, 80 GEN_MODULE_ABC_SCRIPT, 81 AOT_FULL, 82 AOT_PARTIAL, 83 PACKAGES 84} from './pre_define'; 85import { 86 generateMergedAbc, 87 generateNpmEntriesInfo 88} from './gen_merged_abc'; 89import { 90 generateAot, 91 FaultHandler 92} from './gen_aot' 93 94let output: string; 95let isWin: boolean = false; 96let isMac: boolean = false; 97let isDebug: boolean = false; 98let arkDir: string; 99let nodeJs: string; 100 101interface File { 102 path: string, 103 size: number, 104 cacheOutputPath: string, 105 sourceFile: string 106} 107let intermediateJsBundle: Array<File> = []; 108let fileterIntermediateJsBundle: Array<File> = []; 109let moduleInfos: Array<ModuleInfo> = []; 110let filterModuleInfos: Array<ModuleInfo> = []; 111let commonJsModuleInfos: Array<ModuleInfo> = []; 112let ESMModuleInfos: Array<ModuleInfo> = []; 113let entryInfos: Map<string, EntryInfo> = new Map<string, EntryInfo>(); 114let hashJsonObject = {}; 115let moduleHashJsonObject = {}; 116let buildPathInfo: string = ''; 117let buildMapFileList: Set<string> = new Set<string>(); 118let isHotReloadFirstBuild: boolean = true; 119let protoFilePath: string = ''; 120 121const red: string = '\u001b[31m'; 122const reset: string = '\u001b[39m'; 123const blue = '\u001b[34m'; 124const hashFile: string = 'gen_hash.json'; 125const ARK: string = '/ark/'; 126 127export class ModuleInfo { 128 filePath: string; 129 tempFilePath: string; 130 buildFilePath: string; 131 abcFilePath: string; 132 isCommonJs: boolean; 133 recordName: string; 134 sourceFile: string; 135 packageName: string; 136 137 constructor(filePath: string, tempFilePath: string, buildFilePath: string, 138 abcFilePath: string, packageName: string, isCommonJs: boolean) { 139 this.filePath = filePath; 140 this.tempFilePath = tempFilePath; 141 this.buildFilePath = buildFilePath; 142 this.abcFilePath = abcFilePath; 143 this.packageName = packageName; 144 this.isCommonJs = isCommonJs; 145 this.recordName = getOhmUrlByFilepath(filePath, projectConfig, logger); 146 this.sourceFile = filePath.replace(projectConfig.projectRootPath + path.sep, ''); 147 } 148} 149 150export class EntryInfo { 151 npmInfo: string; 152 buildPath: string; 153 entry: string; 154 155 constructor(npmInfo: string, buildPath: string, entry: string) { 156 this.npmInfo = npmInfo; 157 this.buildPath = buildPath; 158 this.entry = entry; 159 } 160} 161 162export class GenAbcPlugin { 163 constructor(output_, arkDir_, nodeJs_, isDebug_) { 164 output = output_; 165 arkDir = arkDir_; 166 nodeJs = nodeJs_; 167 isDebug = isDebug_; 168 } 169 apply(compiler: Compiler) { 170 if (fs.existsSync(path.resolve(arkDir, 'build-win'))) { 171 isWin = true; 172 } else { 173 if (fs.existsSync(path.resolve(arkDir, 'build-mac'))) { 174 isMac = true; 175 } else { 176 if (!fs.existsSync(path.resolve(arkDir, 'build'))) { 177 logger.error(red, 'ArkTS:ERROR find build fail', reset); 178 process.exitCode = FAIL; 179 return; 180 } 181 } 182 } 183 184 if (!checkNodeModules()) { 185 process.exitCode = FAIL; 186 return; 187 } 188 189 if (projectConfig.compileMode === ESMODULE) { 190 if (projectConfig.cachePath && !projectConfig.xtsMode) { 191 let cachedJson: Object = {}; 192 const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON); 193 validateFilePathLength(cachePrebuildInfoPath, logger); 194 cachedJson.buildMode = projectConfig.buildArkMode; 195 cachedJson.bundleName = projectConfig.bundleName; 196 cachedJson.moduleName = projectConfig.moduleName; 197 fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8', 198 (err) => { 199 if (err) { 200 logger.error(red, `ArkTS:ERROR Failed to write module build info.`, reset); 201 } 202 } 203 ); 204 } 205 206 // clear output dir 207 removeDir(output); 208 removeDir(projectConfig.nodeModulesPath); 209 } 210 211 if (projectConfig.compileMode === JSBUNDLE && process.env.minPlatformVersion) { 212 if (projectConfig.cachePath && !projectConfig.xtsMode) { 213 let cachedJson: Object = {}; 214 const cachePrebuildInfoPath: string = path.join(projectConfig.cachePath, PREBUILDINFO_JSON); 215 validateFilePathLength(cachePrebuildInfoPath, logger); 216 cachedJson.minAPIVersion = process.env.minPlatformVersion; 217 fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8', 218 (err) => { 219 if (err) { 220 logger.error(red, `ArkTS:ERROR Failed to write bundle build info.`, reset); 221 } 222 } 223 ); 224 } 225 } 226 227 // for preview mode max listeners 228 events.EventEmitter.defaultMaxListeners = 100; 229 230 compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => { 231 if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) { 232 return; 233 } 234 buildPathInfo = output; 235 compilation.hooks.finishModules.tap('finishModules', handleFinishModules.bind(this)); 236 }); 237 238 compiler.hooks.compilation.tap('GenAbcPlugin', (compilation) => { 239 compilation.hooks.processAssets.tap('processAssets', (assets) => { 240 if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) { 241 return; 242 } 243 Object.keys(compilation.assets).forEach(key => { 244 if (path.extname(key) === EXTNAME_JS || path.extname(key) === EXTNAME_JS_MAP) { 245 delete assets[key]; 246 } 247 }); 248 }); 249 }); 250 251 compiler.hooks.emit.tap('GenAbcPlugin', (compilation) => { 252 if (projectConfig.compileMode === ESMODULE) { 253 return; 254 } 255 Object.keys(compilation.assets).forEach(key => { 256 // choose *.js 257 if (output && path.extname(key) === EXTNAME_JS) { 258 const newContent: string = compilation.assets[key].source(); 259 const keyPath: string = key.replace(/\.js$/, ".temp.js"); 260 writeFileSync(newContent, output, keyPath, key); 261 } 262 }); 263 }); 264 265 compiler.hooks.afterEmit.tap('GenAbcPluginMultiThread', () => { 266 if (projectConfig.compileMode === ESMODULE) { 267 return; 268 } 269 if (intermediateJsBundle.length === 0) { 270 return; 271 } 272 buildPathInfo = output; 273 if (isTs2Abc(projectConfig) || process.env.minPlatformVersion === "8") { 274 invokeWorkersToGenAbc(); 275 } else if (isEs2Abc(projectConfig)){ 276 generateAbcByEs2AbcOfBundleMode(intermediateJsBundle); 277 } else { 278 logger.error(red, `ArkTS:ERROR please set panda module`, reset); 279 } 280 }); 281 } 282} 283 284function clearGlobalInfo() { 285 // fix bug of multi trigger 286 if (process.env.watchMode !== 'true') { 287 intermediateJsBundle = []; 288 moduleInfos = []; 289 entryInfos = new Map<string, EntryInfo>(); 290 } 291 fileterIntermediateJsBundle = []; 292 filterModuleInfos = []; 293 commonJsModuleInfos = []; 294 ESMModuleInfos = []; 295 hashJsonObject = {}; 296 moduleHashJsonObject = {}; 297 buildMapFileList = new Set<string>(); 298} 299 300function getEntryInfo(filePath: string, resourceResolveData: Object): string { 301 if (!resourceResolveData.descriptionFilePath) { 302 return; 303 } 304 305 let isEntry: boolean = false; 306 let mainFileds: Set<string> = getEntryCandidatesFromPackageJson(resourceResolveData); 307 for (let value of mainFileds.values()) { 308 if (toUnixPath(filePath) === value) { 309 isEntry = true; 310 break; 311 } 312 } 313 const packageJsonPath: string = resourceResolveData.descriptionFilePath; 314 let npmInfoPath: string = path.resolve(packageJsonPath, '..'); 315 316 let entry: string = toUnixPath(filePath.replace(npmInfoPath, '')); 317 if (entry.startsWith('/')) { 318 entry = entry.slice(1, entry.length); 319 } 320 321 const fakeEntryPath: string = path.resolve(npmInfoPath, 'fake.js'); 322 const tempFakeEntryPath: string = genTemporaryPath(fakeEntryPath, projectConfig.projectPath, process.env.cachePath, 323 projectConfig); 324 const buildFakeEntryPath: string = genBuildPath(fakeEntryPath, projectConfig.projectPath, projectConfig.buildPath, 325 projectConfig); 326 npmInfoPath = toUnixPath(path.resolve(tempFakeEntryPath, '..')); 327 const buildNpmInfoPath: string = toUnixPath(path.resolve(buildFakeEntryPath, '..')); 328 if (!entryInfos.has(npmInfoPath) && isEntry) { 329 const entryInfo: EntryInfo = new EntryInfo(npmInfoPath, buildNpmInfoPath, entry); 330 entryInfos.set(npmInfoPath, entryInfo); 331 } 332 333 return buildNpmInfoPath; 334} 335 336function getEntryCandidatesFromPackageJson(resourceResolveData: Object): Set<string> { 337 let descriptionFileData: Object = resourceResolveData.descriptionFileData; 338 let packagePath: string = path.resolve(resourceResolveData.descriptionFilePath, '..'); 339 let mainFileds: Set<string> = new Set<string>(); 340 if (descriptionFileData.browser) { 341 if (typeof descriptionFileData.browser === 'string') { 342 mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser))); 343 } else { 344 Object.keys(descriptionFileData.browser).forEach(key => { 345 if (typeof key === 'string' && typeof descriptionFileData.browser[key] === 'string') { 346 mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.browser[key]))); 347 } 348 }); 349 } 350 } 351 if (descriptionFileData.module) { 352 mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.module))); 353 } 354 if (descriptionFileData.main) { 355 mainFileds.add(toUnixPath(path.join(packagePath, descriptionFileData.main))); 356 } 357 if (mainFileds.size === 0) { 358 mainFileds.add(toUnixPath(path.join(packagePath, 'index.js'))); 359 mainFileds.add(toUnixPath(path.join(packagePath, 'index.ets'))); 360 mainFileds.add(toUnixPath(path.join(packagePath, 'index.ts'))); 361 } 362 363 return mainFileds; 364} 365 366function processNodeModulesFile(filePath: string, tempFilePath: string, buildFilePath: string, 367 abcFilePath: string, nodeModulesFile: Array<string>, module: Object): void { 368 let npmPkgPath: string = getEntryInfo(filePath, module.resourceResolveData); 369 const buildNpmPkgPath: string = npmPkgPath.replace(toUnixPath(projectConfig.nodeModulesPath), ''); 370 const npmPkgName: string = toUnixPath(path.join(PACKAGES, buildNpmPkgPath)).replace(new RegExp(NODE_MODULES, 'g'), PACKAGES); 371 372 const descriptionFileData: Object = module.resourceResolveData.descriptionFileData; 373 if (descriptionFileData && descriptionFileData['type'] && descriptionFileData['type'] === 'module') { 374 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false); 375 moduleInfos.push(tempModuleInfo); 376 nodeModulesFile.push(tempFilePath); 377 } else if (filePath.endsWith(EXTNAME_MJS)) { 378 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, false); 379 moduleInfos.push(tempModuleInfo); 380 nodeModulesFile.push(tempFilePath); 381 } else { 382 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, npmPkgName, true); 383 moduleInfos.push(tempModuleInfo); 384 nodeModulesFile.push(tempFilePath); 385 } 386 if (!filePath.endsWith(EXTNAME_JSON)) { 387 buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, ''))); 388 } 389} 390 391function processEtsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void { 392 // skip declaration modules 393 if (filePath.endsWith(EXTNAME_D_ETS)) { 394 return; 395 } 396 if (projectConfig.processTs === true) { 397 tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_TS); 398 buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_TS); 399 } else { 400 tempFilePath = tempFilePath.replace(/\.ets$/, EXTNAME_JS); 401 buildFilePath = buildFilePath.replace(/\.ets$/, EXTNAME_JS); 402 } 403 const abcFilePath: string = genAbcFileName(tempFilePath); 404 if (isPackageModulesFile(filePath, projectConfig)) { 405 processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module); 406 } else { 407 const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1]; 408 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false); 409 moduleInfos.push(tempModuleInfo); 410 } 411 buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, ''))); 412} 413 414function processTsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void { 415 // skip declaration modules 416 if (filePath.endsWith(EXTNAME_D_TS)) { 417 return; 418 } 419 if (projectConfig.processTs === false) { 420 tempFilePath = tempFilePath.replace(/\.ts$/, EXTNAME_JS); 421 buildFilePath = buildFilePath.replace(/\.ts$/, EXTNAME_JS); 422 } 423 const abcFilePath: string = genAbcFileName(tempFilePath); 424 if (isPackageModulesFile(filePath, projectConfig)) { 425 processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module); 426 } else { 427 const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1]; 428 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false); 429 moduleInfos.push(tempModuleInfo); 430 } 431 buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, ''))); 432} 433 434function processJsModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void { 435 const parent: string = path.join(tempFilePath, '..'); 436 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 437 mkDir(parent); 438 } 439 if (filePath.endsWith(EXTNAME_MJS) || filePath.endsWith(EXTNAME_CJS)) { 440 fs.copyFileSync(filePath, tempFilePath); 441 } 442 const abcFilePath: string = genAbcFileName(tempFilePath); 443 if (isPackageModulesFile(filePath, projectConfig)) { 444 processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module); 445 } else { 446 const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1]; 447 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false); 448 moduleInfos.push(tempModuleInfo); 449 } 450 buildMapFileList.add(toUnixPath(filePath.replace(projectConfig.projectRootPath + path.sep, ''))); 451} 452 453function processJsonModule(filePath: string, tempFilePath: string, buildFilePath: string, nodeModulesFile: Array<string>, module: Object): void { 454 const abcFilePath: string = "NA"; 455 if (isPackageModulesFile(filePath, projectConfig)) { 456 processNodeModulesFile(filePath, tempFilePath, buildFilePath, abcFilePath, nodeModulesFile, module); 457 } else { 458 const moduleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[1]; 459 const tempModuleInfo: ModuleInfo = new ModuleInfo(filePath, tempFilePath, buildFilePath, abcFilePath, moduleName, false); 460 moduleInfos.push(tempModuleInfo); 461 } 462} 463 464var cachedSourceMaps: Object; 465 466function updateCachedSourceMaps(): void { 467 const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON); 468 validateFilePathLength(CACHED_SOURCEMAPS, logger); 469 if (!fs.existsSync(CACHED_SOURCEMAPS)) { 470 cachedSourceMaps = {}; 471 } else { 472 cachedSourceMaps = JSON.parse(fs.readFileSync(CACHED_SOURCEMAPS).toString()); 473 } 474 Object.keys(newSourceMaps).forEach(key => { 475 cachedSourceMaps[key] = newSourceMaps[key]; 476 }); 477} 478 479function getCachedModuleList(): Array<string> { 480 const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON); 481 validateFilePathLength(CACHED_MODULELIST_FILE, logger); 482 if (!fs.existsSync(CACHED_MODULELIST_FILE)) { 483 return []; 484 } 485 const data: Object = JSON.parse(fs.readFileSync(CACHED_MODULELIST_FILE).toString()); 486 const moduleList: Array<string> = data.list; 487 return moduleList; 488} 489 490function updateCachedModuleList(moduleList: Array<string>): void { 491 const CACHED_MODULELIST_FILE: string = path.join(process.env.cachePath, MODULELIST_JSON); 492 validateFilePathLength(CACHED_MODULELIST_FILE, logger); 493 const CACHED_SOURCEMAPS: string = path.join(process.env.cachePath, SOURCEMAPS_JSON); 494 validateFilePathLength(CACHED_SOURCEMAPS, logger); 495 let cachedJson: Object = {}; 496 cachedJson["list"] = moduleList; 497 fs.writeFile(CACHED_MODULELIST_FILE, JSON.stringify(cachedJson, null, 2), 'utf-8', 498 (err) => { 499 if (err) { 500 logger.error(red, `ArkTS:ERROR Failed to write module list.`, reset); 501 } 502 } 503 ); 504 fs.writeFile(CACHED_SOURCEMAPS, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8', 505 (err) => { 506 if (err) { 507 logger.error(red, `ArkTS:ERROR Failed to write cache sourceMaps json.`, reset); 508 } 509 } 510 ); 511} 512 513function writeSourceMaps(): void { 514 mkdirsSync(projectConfig.buildPath); 515 let sourceMapFilePath: string = path.join(projectConfig.buildPath, SOURCEMAPS); 516 validateFilePathLength(sourceMapFilePath, logger); 517 fs.writeFile(sourceMapFilePath, JSON.stringify(cachedSourceMaps, null, 2), 'utf-8', 518 (err) => { 519 if (err) { 520 logger.error(red, `ArkTS:ERROR Failed to write sourceMaps.`, reset); 521 } 522 } 523 ); 524} 525 526function eliminateUnusedFiles(moduleList: Array<string>): void{ 527 let cachedModuleList: Array<string> = getCachedModuleList(); 528 if (cachedModuleList.length !== 0) { 529 const eliminateFiles: Array<string> = cachedModuleList.filter(m => !moduleList.includes(m)); 530 eliminateFiles.forEach((file) => { 531 delete cachedSourceMaps[file]; 532 }); 533 } 534} 535 536function handleFullModuleFiles(modules, callback): void { 537 const nodeModulesFile: Array<string> = []; 538 modules.forEach(module => { 539 if (module !== undefined && module.resourceResolveData !== undefined) { 540 const filePath: string = module.resourceResolveData.path; 541 let tempFilePath = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath, projectConfig); 542 if (tempFilePath.length === 0) { 543 return; 544 } 545 validateFilePathLength(tempFilePath, logger); 546 let buildFilePath: string = genBuildPath(filePath, projectConfig.projectPath, projectConfig.buildPath, 547 projectConfig); 548 validateFilePathLength(buildFilePath, logger); 549 tempFilePath = toUnixPath(tempFilePath); 550 buildFilePath = toUnixPath(buildFilePath); 551 552 switch (path.extname(filePath)) { 553 case EXTNAME_ETS: { 554 processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module); 555 break; 556 } 557 case EXTNAME_TS: { 558 processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module); 559 break; 560 } 561 case EXTNAME_JS: 562 case EXTNAME_MJS: 563 case EXTNAME_CJS: { 564 processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module); 565 break; 566 } 567 case EXTNAME_JSON: { 568 processJsonModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, module); 569 break; 570 } 571 default: { 572 logger.error(red, `ArkTS:ERROR Cannot find resolve this file path: ${filePath}`, reset); 573 process.exitCode = FAIL; 574 } 575 } 576 } 577 }); 578 579 // for mergeabc source maps 580 if (projectConfig.buildArkMode === 'debug') { 581 const moduleList: Array<string> = Array.from(buildMapFileList); 582 updateCachedSourceMaps(); 583 eliminateUnusedFiles(moduleList); 584 updateCachedModuleList(moduleList); 585 writeSourceMaps(); 586 } 587 588 if (process.env.panda !== TS2ABC) { 589 const outputABCPath: string = path.join(projectConfig.buildPath, MODULES_ABC); 590 validateFilePathLength(outputABCPath, logger); 591 generateMergedAbc(moduleInfos, entryInfos, outputABCPath); 592 clearGlobalInfo(); 593 } else { 594 invokeWorkersModuleToGenAbc(moduleInfos); 595 } 596} 597 598function processEntryToGenAbc(entryInfos: Map<string, EntryInfo>): void { 599 if (entryInfos.size <= 0) { 600 return; 601 } 602 generateNpmEntriesInfo(entryInfos); 603 const npmEntriesInfoPath: string = path.join(process.env.cachePath, NPMENTRIES_TXT); 604 validateFilePathLength(npmEntriesInfoPath, logger); 605 let npmEntriesProtoFileName: string = "npm_entries" + EXTNAME_PROTO_BIN; 606 const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, "protos", "npm_entries", npmEntriesProtoFileName); 607 validateFilePathLength(npmEntriesProtoFilePath, logger); 608 mkdirsSync(path.dirname(npmEntriesProtoFilePath)); 609 let js2Abc: string = path.join(arkDir, 'build', 'bin', 'js2abc'); 610 if (isWin) { 611 js2Abc = path.join(arkDir, 'build-win', 'bin', 'js2abc.exe'); 612 } else if (isMac) { 613 js2Abc = path.join(arkDir, 'build-mac', 'bin', 'js2abc'); 614 } 615 validateFilePathLength(js2Abc, logger); 616 const singleCmd: string = `"${js2Abc}" --compile-npm-entries "${npmEntriesInfoPath}" "${npmEntriesProtoFilePath}`; 617 try { 618 childProcess.execSync(singleCmd); 619 } catch (e) { 620 logger.debug(red, `ArkTS:ERROR Failed to generate npm proto file to abc, Error message: ${e}`, reset); 621 } 622} 623 624function writeFileSync(inputString: string, buildPath: string, keyPath: string, jsBundleFile: string): void { 625 let output = path.resolve(buildPath, keyPath); 626 validateFilePathLength(output, logger); 627 let parent: string = path.join(output, '..'); 628 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 629 mkDir(parent); 630 } 631 let cacheOutputPath: string = ""; 632 if (process.env.cachePath) { 633 let buildDirArr: string[] = projectConfig.buildPath.split(path.sep); 634 let abilityDir: string = buildDirArr[buildDirArr.length - 1]; 635 cacheOutputPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, keyPath); 636 } else { 637 cacheOutputPath = output; 638 } 639 validateFilePathLength(cacheOutputPath, logger); 640 parent = path.join(cacheOutputPath, '..'); 641 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 642 mkDir(parent); 643 } 644 fs.writeFileSync(cacheOutputPath, inputString); 645 if (fs.existsSync(cacheOutputPath)) { 646 const fileSize: number = fs.statSync(cacheOutputPath).size; 647 let sourceFile: string = output.replace(/\.temp\.js$/, "_.js"); 648 if (!isDebug && projectConfig.projectRootPath) { 649 sourceFile = toUnixPath(sourceFile.replace(projectConfig.projectRootPath + path.sep, '')); 650 } else { 651 sourceFile = toUnixPath(sourceFile); 652 } 653 output = toUnixPath(output); 654 cacheOutputPath = toUnixPath(cacheOutputPath); 655 656 intermediateJsBundle.push({path: output, size: fileSize, cacheOutputPath: cacheOutputPath, sourceFile: sourceFile}); 657 } else { 658 logger.debug(red, `ArkTS:ERROR Failed to convert file ${jsBundleFile} to bin. ${output} is lost`, reset); 659 process.exitCode = FAIL; 660 } 661} 662 663function mkDir(path_: string): void { 664 const parent: string = path.join(path_, '..'); 665 if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) { 666 mkDir(parent); 667 } 668 fs.mkdirSync(path_); 669} 670 671function getSmallestSizeGroup(groupSize: Map<number, number>): any { 672 const groupSizeArray: any = Array.from(groupSize); 673 groupSizeArray.sort(function(g1, g2) { 674 return g1[1] - g2[1]; // sort by size 675 }); 676 return groupSizeArray[0][0]; 677} 678 679function splitJsBundlesBySize(bundleArray: Array<File>, groupNumber: number): any { 680 const result: any = []; 681 if (bundleArray.length < groupNumber) { 682 for (const value of bundleArray) { 683 result.push([value]); 684 } 685 return result; 686 } 687 688 bundleArray.sort(function(f1: File, f2: File) { 689 return f2.size - f1.size; 690 }); 691 const groupFileSize = new Map(); 692 for (let i = 0; i < groupNumber; ++i) { 693 result.push([]); 694 groupFileSize.set(i, 0); 695 } 696 697 let index = 0; 698 while (index < bundleArray.length) { 699 const smallestGroup: any = getSmallestSizeGroup(groupFileSize); 700 result[smallestGroup].push(bundleArray[index]); 701 const sizeUpdate: any = groupFileSize.get(smallestGroup) + bundleArray[index].size; 702 groupFileSize.set(smallestGroup, sizeUpdate); 703 index++; 704 } 705 return result; 706} 707 708function invokeWorkersModuleToGenAbc(moduleInfos: Array<ModuleInfo>): void { 709 invokeClusterModuleToAbc(); 710} 711 712export function initAbcEnv() : string[] { 713 let args: string[] = []; 714 if (process.env.minPlatformVersion === "8") { 715 process.env.panda = TS2ABC; 716 let js2abc: string = path.join(arkDir, 'build', 'legacy_api8', 'src', 'index.js'); 717 if (isWin) { 718 js2abc = path.join(arkDir, 'build-win', 'legacy_api8', 'src', 'index.js'); 719 } else if (isMac) { 720 js2abc = path.join(arkDir, 'build-mac', 'legacy_api8', 'src', 'index.js'); 721 } 722 validateFilePathLength(js2abc, logger); 723 724 js2abc = '"' + js2abc + '"'; 725 args = [ 726 '--expose-gc', 727 js2abc 728 ]; 729 if (isDebug) { 730 args.push('--debug'); 731 } 732 } else if (process.env.panda === TS2ABC) { 733 let js2abc: string = path.join(arkDir, 'build', 'src', 'index.js'); 734 if (isWin) { 735 js2abc = path.join(arkDir, 'build-win', 'src', 'index.js'); 736 } else if (isMac) { 737 js2abc = path.join(arkDir, 'build-mac', 'src', 'index.js'); 738 } 739 validateFilePathLength(js2abc, logger); 740 741 js2abc = '"' + js2abc + '"'; 742 args = [ 743 '--expose-gc', 744 js2abc 745 ]; 746 if (isDebug) { 747 args.push('--debug'); 748 } 749 } else if (process.env.panda === ES2ABC || process.env.panda === 'undefined' || process.env.panda === undefined) { 750 let es2abc: string = path.join(arkDir, 'build', 'bin', 'es2abc'); 751 if (isWin) { 752 es2abc = path.join(arkDir, 'build-win', 'bin', 'es2abc.exe'); 753 } else if (isMac) { 754 es2abc = path.join(arkDir, 'build-mac', 'bin', 'es2abc'); 755 } 756 validateFilePathLength(es2abc, logger); 757 758 args = [ 759 '"' + es2abc + '"' 760 ]; 761 if (isDebug) { 762 args.push('--debug-info'); 763 } 764 if (projectConfig.compileMode === ESMODULE) { 765 args.push('--merge-abc'); 766 } 767 } else { 768 logger.error(red, `ArkTS:ERROR please set panda module`, reset); 769 } 770 771 return args; 772} 773 774function invokeClusterModuleToAbc(): void { 775 if (process.env.watchMode === 'true') { 776 process.exitCode = SUCCESS; 777 } 778 filterIntermediateModuleByHashJson(buildPathInfo, moduleInfos); 779 const abcArgs: string[] = initAbcEnv(); 780 781 const splitedModules: any[] = splitModulesByNumber(filterModuleInfos, MAX_WORKER_NUMBER); 782 let cmdPrefix: string = `${nodeJs} ${abcArgs.join(' ')}`; 783 const workerNumber: number = MAX_WORKER_NUMBER < splitedModules.length ? MAX_WORKER_NUMBER : splitedModules.length; 784 785 try { 786 if (process.env.watchMode === 'true') { 787 processWorkersOfPreviewMode(splitedModules, cmdPrefix, workerNumber); 788 } else { 789 processWorkersOfBuildMode(splitedModules, cmdPrefix, workerNumber); 790 } 791 } catch (e) { 792 logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset); 793 process.env.abcCompileSuccess = 'false'; 794 if (process.env.watchMode !== 'true') { 795 process.exit(FAIL); 796 } 797 } 798} 799 800function splitModulesByNumber(moduleInfos: Array<ModuleInfo>, workerNumber: number): any[] { 801 const result: any = []; 802 if (moduleInfos.length < workerNumber) { 803 for (const value of moduleInfos) { 804 result.push([value]); 805 } 806 return result; 807 } 808 809 for (let i = 0; i < workerNumber; ++i) { 810 result.push([]); 811 } 812 813 for (let i = 0; i < moduleInfos.length; i++) { 814 const chunk = i % workerNumber; 815 result[chunk].push(moduleInfos[i]); 816 } 817 818 return result; 819} 820 821function invokeWorkersToGenAbc(): void { 822 if (process.env.watchMode === 'true') { 823 process.exitCode = SUCCESS; 824 } 825 let cmdPrefix: string = ''; 826 827 const abcArgs: string[] = initAbcEnv(); 828 if (process.env.panda === TS2ABC) { 829 cmdPrefix = `${nodeJs} ${abcArgs.join(' ')}`; 830 } else { 831 logger.error(red, `ArkTS:ERROR please set panda module`, reset); 832 } 833 834 filterIntermediateJsBundleByHashJson(buildPathInfo, intermediateJsBundle); 835 const splitedBundles: any[] = splitJsBundlesBySize(fileterIntermediateJsBundle, MAX_WORKER_NUMBER); 836 const workerNumber: number = MAX_WORKER_NUMBER < splitedBundles.length ? MAX_WORKER_NUMBER : splitedBundles.length; 837 838 try { 839 if (process.env.watchMode === 'true') { 840 processWorkersOfPreviewMode(splitedBundles, cmdPrefix, workerNumber); 841 } else { 842 processWorkersOfBuildMode(splitedBundles, cmdPrefix, workerNumber); 843 } 844 } catch (e) { 845 logger.debug(red, `ArkTS:ERROR failed to generate abc. Error message: ${e}`, reset); 846 process.env.abcCompileSuccess = 'false'; 847 if (process.env.watchMode !== 'true') { 848 process.exit(FAIL); 849 } 850 } 851} 852 853function filterIntermediateModuleByHashJson(buildPath: string, moduleInfos: Array<ModuleInfo>): void { 854 const tempModuleInfos = Array<ModuleInfo>(); 855 moduleInfos.forEach((item) => { 856 const check = tempModuleInfos.every((newItem) => { 857 return item.tempFilePath !== newItem.tempFilePath; 858 }); 859 if (check) { 860 tempModuleInfos.push(item); 861 } 862 }); 863 moduleInfos = tempModuleInfos; 864 865 for (let i = 0; i < moduleInfos.length; ++i) { 866 filterModuleInfos.push(moduleInfos[i]); 867 } 868 const hashFilePath: string = genHashJsonPath(buildPath); 869 if (hashFilePath.length === 0) { 870 return; 871 } 872 const updateJsonObject: Object = {}; 873 let jsonObject: Object = {}; 874 let jsonFile: string = ''; 875 if (fs.existsSync(hashFilePath)) { 876 jsonFile = fs.readFileSync(hashFilePath).toString(); 877 jsonObject = JSON.parse(jsonFile); 878 filterModuleInfos = []; 879 for (let i = 0; i < moduleInfos.length; ++i) { 880 const input: string = moduleInfos[i].tempFilePath; 881 let outputPath: string = genProtoFileName(moduleInfos[i].tempFilePath); 882 if (!fs.existsSync(input)) { 883 logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset); 884 process.exitCode = FAIL; 885 break; 886 } 887 if (fs.existsSync(outputPath)) { 888 const hashInputContentData: string = toHashData(input); 889 const hashAbcContentData: string = toHashData(outputPath); 890 if (jsonObject[input] === hashInputContentData && jsonObject[outputPath] === hashAbcContentData) { 891 updateJsonObject[input] = hashInputContentData; 892 updateJsonObject[outputPath] = hashAbcContentData; 893 } else { 894 filterModuleInfos.push(moduleInfos[i]); 895 } 896 } else { 897 filterModuleInfos.push(moduleInfos[i]); 898 } 899 } 900 } 901 902 moduleHashJsonObject = updateJsonObject; 903} 904 905function writeModuleHashJson(): void { 906 for (let i = 0; i < filterModuleInfos.length; ++i) { 907 const input: string = filterModuleInfos[i].tempFilePath; 908 let outputPath: string = genProtoFileName(filterModuleInfos[i].tempFilePath);; 909 if (!fs.existsSync(input) || !fs.existsSync(outputPath)) { 910 logger.debug(red, `ArkTS:ERROR ${input} is lost`, reset); 911 process.exitCode = FAIL; 912 break; 913 } 914 const hashInputContentData: string = toHashData(input); 915 const hashOutputContentData: string = toHashData(outputPath); 916 moduleHashJsonObject[input] = hashInputContentData; 917 moduleHashJsonObject[outputPath] = hashOutputContentData; 918 } 919 const hashFilePath: string = genHashJsonPath(buildPathInfo); 920 if (hashFilePath.length === 0) { 921 return; 922 } 923 // fix bug of multi trigger 924 fs.writeFileSync(hashFilePath, JSON.stringify(moduleHashJsonObject)); 925} 926 927function filterIntermediateJsBundleByHashJson(buildPath: string, inputPaths: File[]): void { 928 inputPaths = removeDuplicateInfoOfBundleList(inputPaths); 929 930 for (let i = 0; i < inputPaths.length; ++i) { 931 fileterIntermediateJsBundle.push(inputPaths[i]); 932 } 933 const hashFilePath: string = genHashJsonPath(buildPath); 934 if (hashFilePath.length === 0) { 935 return; 936 } 937 const updateJsonObject: Object = {}; 938 let jsonObject: Object = {}; 939 let jsonFile: string = ''; 940 if (fs.existsSync(hashFilePath)) { 941 jsonFile = fs.readFileSync(hashFilePath).toString(); 942 jsonObject = JSON.parse(jsonFile); 943 fileterIntermediateJsBundle = []; 944 for (let i = 0; i < inputPaths.length; ++i) { 945 const cacheOutputPath: string = inputPaths[i].cacheOutputPath; 946 const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc'); 947 if (!fs.existsSync(cacheOutputPath)) { 948 logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset); 949 process.exitCode = FAIL; 950 break; 951 } 952 if (fs.existsSync(cacheAbcFilePath)) { 953 const hashInputContentData: string = toHashData(cacheOutputPath); 954 const hashAbcContentData: string = toHashData(cacheAbcFilePath); 955 if (jsonObject[cacheOutputPath] === hashInputContentData && jsonObject[cacheAbcFilePath] === hashAbcContentData) { 956 updateJsonObject[cacheOutputPath] = hashInputContentData; 957 updateJsonObject[cacheAbcFilePath] = hashAbcContentData; 958 } else { 959 fileterIntermediateJsBundle.push(inputPaths[i]); 960 } 961 } else { 962 fileterIntermediateJsBundle.push(inputPaths[i]); 963 } 964 } 965 } 966 967 hashJsonObject = updateJsonObject; 968} 969 970function writeHashJson(): void { 971 for (let i = 0; i < fileterIntermediateJsBundle.length; ++i) { 972 const cacheOutputPath: string = fileterIntermediateJsBundle[i].cacheOutputPath; 973 const cacheAbcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, '.abc'); 974 if (!fs.existsSync(cacheOutputPath) || !fs.existsSync(cacheAbcFilePath)) { 975 logger.debug(red, `ArkTS:ERROR ${cacheOutputPath} is lost`, reset); 976 process.exitCode = FAIL; 977 break; 978 } 979 const hashInputContentData: string = toHashData(cacheOutputPath); 980 const hashAbcContentData: string = toHashData(cacheAbcFilePath); 981 hashJsonObject[cacheOutputPath] = hashInputContentData; 982 hashJsonObject[cacheAbcFilePath] = hashAbcContentData; 983 } 984 const hashFilePath: string = genHashJsonPath(buildPathInfo); 985 if (hashFilePath.length === 0) { 986 return; 987 } 988 fs.writeFileSync(hashFilePath, JSON.stringify(hashJsonObject)); 989} 990 991function genHashJsonPath(buildPath: string): string { 992 buildPath = toUnixPath(buildPath); 993 if (process.env.cachePath) { 994 if (!fs.existsSync(process.env.cachePath) || !fs.statSync(process.env.cachePath).isDirectory()) { 995 logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset); 996 return ''; 997 } 998 let buildDirArr: string[] = projectConfig.buildPath.split(path.sep); 999 let abilityDir: string = buildDirArr[buildDirArr.length - 1]; 1000 let hashJsonPath: string = path.join(process.env.cachePath, TEMPORARY, abilityDir, hashFile); 1001 validateFilePathLength(hashJsonPath, logger) 1002 mkdirsSync(path.dirname(hashJsonPath)); 1003 return hashJsonPath; 1004 } else if (buildPath.indexOf(ARK) >= 0) { 1005 const dataTmps: string[] = buildPath.split(ARK); 1006 const hashPath: string = path.join(dataTmps[0], ARK); 1007 if (!fs.existsSync(hashPath) || !fs.statSync(hashPath).isDirectory()) { 1008 logger.debug(red, `ArkTS:ERROR hash path does not exist`, reset); 1009 return ''; 1010 } 1011 let hashJsonPath: string = path.join(hashPath, hashFile); 1012 validateFilePathLength(hashJsonPath, logger); 1013 return hashJsonPath; 1014 } else { 1015 logger.debug(red, `ArkTS:ERROR not cache exist`, reset); 1016 return ''; 1017 } 1018} 1019 1020function checkNodeModules() { 1021 if (process.env.panda === TS2ABC) { 1022 let arkEntryPath: string = path.join(arkDir, 'build'); 1023 if (isWin) { 1024 arkEntryPath = path.join(arkDir, 'build-win'); 1025 } else if (isMac) { 1026 arkEntryPath = path.join(arkDir, 'build-mac'); 1027 } 1028 let nodeModulesPath: string = path.join(arkEntryPath, NODE_MODULES); 1029 validateFilePathLength(nodeModulesPath, logger); 1030 if (!(fs.existsSync(nodeModulesPath) && fs.statSync(nodeModulesPath).isDirectory())) { 1031 logger.error(red, `ERROR: node_modules for ark compiler not found. 1032 Please make sure switch to non-root user before runing "npm install" for safity requirements and try re-run "npm install" under ${arkEntryPath}`, reset); 1033 return false; 1034 } 1035 } 1036 1037 return true; 1038} 1039 1040function copyFileCachePathToBuildPath() { 1041 for (let i = 0; i < intermediateJsBundle.length; ++i) { 1042 const abcFile: string = intermediateJsBundle[i].path.replace(/\.temp\.js$/, ".abc"); 1043 const cacheOutputPath: string = intermediateJsBundle[i].cacheOutputPath; 1044 const cacheAbcFilePath: string = intermediateJsBundle[i].cacheOutputPath.replace(/\.temp\.js$/, ".abc"); 1045 if (!fs.existsSync(cacheAbcFilePath)) { 1046 logger.debug(red, `ArkTS:ERROR ${cacheAbcFilePath} is lost`, reset); 1047 process.exitCode = FAIL; 1048 break; 1049 } 1050 let parent: string = path.join(abcFile, '..'); 1051 if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) { 1052 mkDir(parent); 1053 } 1054 // for preview mode, cache path and old abc file both exist, should copy abc file for updating 1055 if (process.env.cachePath !== undefined) { 1056 fs.copyFileSync(cacheAbcFilePath, abcFile); 1057 } 1058 if (process.env.cachePath === undefined && fs.existsSync(cacheOutputPath)) { 1059 fs.unlinkSync(cacheOutputPath); 1060 } 1061 } 1062} 1063 1064function processExtraAsset() { 1065 if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) { 1066 writeHashJson(); 1067 copyFileCachePathToBuildPath(); 1068 } else if (projectConfig.compileMode === ESMODULE) { 1069 processEntryToGenAbc(entryInfos); 1070 writeModuleHashJson(); 1071 copyModuleFileCachePathToBuildPath(); 1072 mergeProtoToAbc(); 1073 } 1074 clearGlobalInfo(); 1075} 1076 1077function handleHotReloadChangedFiles() { 1078 if (!fs.existsSync(projectConfig.changedFileList)) { 1079 logger.debug(blue, `ArkTS: Cannot find file: ${projectConfig.changedFileList}, skip hot reload build`, reset); 1080 return; 1081 } 1082 1083 let changedFileListJson: string = fs.readFileSync(projectConfig.changedFileList).toString(); 1084 let changedFileList: Array<string> = JSON.parse(changedFileListJson).modifiedFiles; 1085 if (typeof(changedFileList) == "undefined" || changedFileList.length == 0) { 1086 return; 1087 } 1088 1089 let relativeProjectPath = projectConfig.projectPath.slice(projectConfig.projectRootPath.length + path.sep.length); 1090 const nodeModulesFile: Array<string> = []; 1091 let hotReloadSourceMap: Object = {}; 1092 moduleInfos = []; 1093 1094 for (let file of changedFileList) { 1095 let filePath: string = path.join(projectConfig.projectPath, file); 1096 validateFilePathLength(filePath, logger); 1097 let tempFilePath: string = genTemporaryPath(filePath, projectConfig.projectPath, process.env.cachePath, projectConfig); 1098 if (tempFilePath.length === 0) { 1099 return; 1100 } 1101 validateFilePathLength(tempFilePath, logger); 1102 let buildFilePath: string = ""; 1103 tempFilePath = toUnixPath(tempFilePath); 1104 1105 switch (path.extname(filePath)) { 1106 case EXTNAME_ETS: { 1107 processEtsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined); 1108 break; 1109 } 1110 case EXTNAME_TS: { 1111 processTsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined); 1112 break; 1113 } 1114 case EXTNAME_JS: 1115 case EXTNAME_MJS: 1116 case EXTNAME_CJS: { 1117 processJsModule(filePath, tempFilePath, buildFilePath, nodeModulesFile, undefined); 1118 break; 1119 } 1120 case EXTNAME_JSON: { 1121 logger.debug(blue, `ArkTS: json source file: ${filePath} changed, skip hot reload build`, reset); 1122 return; 1123 } 1124 default: { 1125 logger.debug(blue, `ArkTS:ERROR Cannot resolve file path: ${filePath}, stop hot reload build`, reset); 1126 return; 1127 } 1128 } 1129 1130 let sourceMapPath: string = toUnixPath(path.join(relativeProjectPath, file)); 1131 validateFilePathLength(sourceMapPath, logger); 1132 hotReloadSourceMap[sourceMapPath] = newSourceMaps[sourceMapPath]; 1133 } 1134 1135 if (!fs.existsSync(projectConfig.patchAbcPath)) { 1136 mkdirsSync(projectConfig.patchAbcPath); 1137 } 1138 1139 const outputABCPath: string = path.join(projectConfig.patchAbcPath, MODULES_ABC); 1140 validateFilePathLength(outputABCPath, logger); 1141 generateMergedAbc(moduleInfos, entryInfos, outputABCPath); 1142 1143 // write source maps 1144 let sourceMapFilePath: string = path.join(projectConfig.patchAbcPath, SOURCEMAPS); 1145 validateFilePathLength(sourceMapFilePath, logger); 1146 fs.writeFileSync(sourceMapFilePath, 1147 JSON.stringify(hotReloadSourceMap, null, 2), 'utf-8'); 1148} 1149 1150function handleFinishModules(modules, callback) { 1151 if (projectConfig.hotReload && !isHotReloadFirstBuild) { 1152 handleHotReloadChangedFiles(); 1153 return; 1154 } 1155 1156 handleFullModuleFiles(modules, callback); 1157 1158 if (projectConfig.hotReload) { 1159 isHotReloadFirstBuild = false; 1160 } 1161} 1162 1163function copyModuleFileCachePathToBuildPath(): void { 1164 protoFilePath = path.join(path.join(process.env.cachePath, "protos", PROTO_FILESINFO_TXT)); 1165 validateFilePathLength(protoFilePath, logger); 1166 mkdirsSync(path.dirname(protoFilePath)); 1167 let entriesInfo: string = ''; 1168 moduleInfos = removeDuplicateInfo(moduleInfos); 1169 moduleInfos.sort((m1: ModuleInfo, m2: ModuleInfo) => { 1170 return m1.tempFilePath < m2.tempFilePath ? 1 : -1; 1171 }); 1172 for (let i = 0; i < moduleInfos.length; ++i) { 1173 let protoTempPath: string = genProtoFileName(moduleInfos[i].tempFilePath); 1174 entriesInfo += `${toUnixPath(protoTempPath)}\n`; 1175 } 1176 if (entryInfos.size > 0) { 1177 let npmEntriesProtoFileName: string = "npm_entries" + EXTNAME_PROTO_BIN; 1178 const npmEntriesProtoFilePath: string = path.join(process.env.cachePath, "protos", "npm_entries", npmEntriesProtoFileName); 1179 entriesInfo += `${toUnixPath(npmEntriesProtoFilePath)}\n`; 1180 } 1181 fs.writeFileSync(protoFilePath, entriesInfo, 'utf-8'); 1182} 1183 1184function mergeProtoToAbc(): void { 1185 let mergeAbc: string = path.join(arkDir, 'build', 'bin', 'merge_abc'); 1186 if (isWin) { 1187 mergeAbc = path.join(arkDir, 'build-win', 'bin', 'merge_abc.exe'); 1188 } else if (isMac) { 1189 mergeAbc = path.join(arkDir, 'build-mac', 'bin', 'merge_abc'); 1190 } 1191 mkdirsSync(projectConfig.buildPath); 1192 const singleCmd: string = `"${mergeAbc}" --input "@${protoFilePath}" --outputFilePath "${projectConfig.buildPath}" --output ${MODULES_ABC} --suffix protoBin`; 1193 try { 1194 childProcess.execSync(singleCmd); 1195 } catch (e) { 1196 logger.debug(red, `ArkTS:ERROR Failed to merge proto file to abc. Error message: ${e}`, reset); 1197 } 1198} 1199 1200function generateAbcByEs2AbcOfBundleMode(inputPaths: File[]) { 1201 filterIntermediateJsBundleByHashJson(buildPathInfo, inputPaths); 1202 if (fileterIntermediateJsBundle.length === 0) { 1203 processExtraAsset(); 1204 return; 1205 } 1206 let filesInfoPath = generateFileOfBundle(fileterIntermediateJsBundle); 1207 const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16; 1208 let genAbcCmd: string = 1209 `${initAbcEnv().join(' ')} "@${filesInfoPath}" --file-threads "${fileThreads}"`; 1210 logger.debug('gen abc cmd is: ', genAbcCmd); 1211 try { 1212 if (process.env.watchMode === 'true') { 1213 childProcess.execSync(genAbcCmd); 1214 processExtraAsset(); 1215 } else { 1216 const child = childProcess.exec(genAbcCmd); 1217 child.on('exit', (code: any) => { 1218 if (code === FAIL) { 1219 logger.debug(red, "ArkTS:ERROR failed to execute es2abc", reset); 1220 process.exit(FAIL); 1221 } 1222 if (process.env.cachePath === undefined) { 1223 unlinkSync(filesInfoPath); 1224 } 1225 processExtraAsset(); 1226 }); 1227 1228 child.on('error', (err: any) => { 1229 logger.debug(red, err.toString(), reset); 1230 process.exit(FAIL); 1231 }); 1232 1233 child.stderr.on('data', (data: any) => { 1234 logger.error(red, data.toString(), reset); 1235 }); 1236 } 1237 } catch (e) { 1238 logger.debug(red, `ArkTS:ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e} `, reset); 1239 process.env.abcCompileSuccess = 'false'; 1240 if (process.env.watchMode !== 'true') { 1241 process.exit(FAIL); 1242 } 1243 } finally { 1244 if (process.env.watchMode === 'true') { 1245 if (process.env.cachePath === undefined) { 1246 unlinkSync(filesInfoPath); 1247 } 1248 } 1249 } 1250} 1251 1252function generateFileOfBundle(inputPaths: File[]): string { 1253 let filesInfoPath: string = buildCachePath(FILESINFO_TXT, projectConfig, logger); 1254 inputPaths = removeDuplicateInfoOfBundleList(inputPaths); 1255 1256 let filesInfo: string = ''; 1257 inputPaths.forEach(info => { 1258 const cacheOutputPath: string = info.cacheOutputPath; 1259 const recordName: string = 'null_recordName'; 1260 const moduleType: string = 'script'; 1261 const sourceFile: string = info.sourceFile; 1262 const abcFilePath: string = cacheOutputPath.replace(/\.temp\.js$/, ".abc"); 1263 filesInfo += `${cacheOutputPath};${recordName};${moduleType};${sourceFile};${abcFilePath}\n`; 1264 }); 1265 fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8'); 1266 1267 return filesInfoPath; 1268} 1269 1270function removeDuplicateInfoOfBundleList(inputPaths: File[]) { 1271 const tempInputPaths = Array<File>(); 1272 inputPaths.forEach((item) => { 1273 const check = tempInputPaths.every((newItem) => { 1274 return item.path !== newItem.path; 1275 }); 1276 if (check) { 1277 tempInputPaths.push(item); 1278 } 1279 }); 1280 inputPaths = tempInputPaths; 1281 1282 return inputPaths; 1283} 1284 1285function processWorkersOfPreviewMode(splittedData: any, cmdPrefix: string, workerNumber: number) { 1286 let processEnv: any = Object.assign({}, process.env); 1287 let arkEnvParams: any = { 1288 'splittedData': JSON.stringify(splittedData), 1289 'cmdPrefix': cmdPrefix, 1290 'workerNumber': workerNumber.toString(), 1291 }; 1292 if (projectConfig.compileMode === JSBUNDLE || projectConfig.compileMode === undefined) { 1293 arkEnvParams['mode'] = JSBUNDLE; 1294 } else if (projectConfig.compileMode === ESMODULE) { 1295 arkEnvParams['cachePath'] = process.env.cachePath; 1296 arkEnvParams['mode'] = ESMODULE; 1297 } 1298 processEnv.arkEnvParams = JSON.stringify(arkEnvParams); 1299 1300 let genAbcCmd: string = `${nodeJs} "${path.resolve(__dirname, MANAGE_WORKERS_SCRIPT)}"`; 1301 childProcess.execSync(genAbcCmd, {env: processEnv}); 1302 processExtraAsset(); 1303} 1304 1305function processWorkersOfBuildMode(splittedData: any, cmdPrefix: string, workerNumber: number) { 1306 const useNewApi: boolean = nodeLargeOrEqualTargetVersion(16); 1307 1308 if (useNewApi && cluster.isPrimary || !useNewApi && cluster.isMaster) { 1309 let genAbcScript: string = GEN_ABC_SCRIPT; 1310 if (projectConfig.compileMode === ESMODULE) { 1311 genAbcScript = GEN_MODULE_ABC_SCRIPT; 1312 } 1313 if (useNewApi) { 1314 cluster.setupPrimary({ 1315 exec: path.resolve(__dirname, genAbcScript) 1316 }); 1317 } else { 1318 cluster.setupMaster({ 1319 exec: path.resolve(__dirname, genAbcScript) 1320 }); 1321 } 1322 1323 for (let i = 0; i < workerNumber; ++i) { 1324 let workerData: any = { 1325 'inputs': JSON.stringify(splittedData[i]), 1326 'cmd': cmdPrefix 1327 }; 1328 if (projectConfig.compileMode === ESMODULE) { 1329 let sn: number = i + 1; 1330 let workerFileName: string = `filesInfo_${sn}.txt`; 1331 workerData['workerFileName'] = workerFileName; 1332 workerData['cachePath'] = process.env.cachePath; 1333 } 1334 cluster.fork(workerData); 1335 } 1336 1337 cluster.on('exit', (worker, code, signal) => { 1338 if (code === FAIL) { 1339 process.exitCode = FAIL; 1340 } 1341 logger.debug(`worker ${worker.process.pid} finished`); 1342 }); 1343 1344 process.on('exit', (code) => { 1345 if (process.exitCode !== FAIL && process.env.watchMode !== 'true') { 1346 processExtraAsset(); 1347 if (projectConfig.compileMode === ESMODULE && 1348 (projectConfig.anBuildMode === AOT_FULL || projectConfig.anBuildMode === AOT_PARTIAL)) { 1349 let faultHandler: FaultHandler = (error) => { logger.error(error); process.exit(FAIL); } 1350 let abcArgs: string[] = initAbcEnv(); 1351 abcArgs.unshift(nodeJs); 1352 const appAbc: string = path.join(projectConfig.buildPath, MODULES_ABC); 1353 generateAot(arkDir, appAbc, projectConfig, logger, faultHandler); 1354 } 1355 } 1356 }); 1357 } 1358} 1359