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