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