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