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