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