• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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 childProcess from 'child_process';
17import fs from 'fs';
18import path from 'path';
19import cluster from 'cluster';
20
21import {
22  COMMONJS,
23  ESM,
24  ESMODULE,
25  EXTNAME_CJS,
26  EXTNAME_ETS,
27  EXTNAME_JS,
28  EXTNAME_JSON,
29  EXTNAME_MJS,
30  EXTNAME_PROTO_BIN,
31  EXTNAME_TS,
32  EXTNAME_TXT,
33  FAIL,
34  FILESINFO,
35  FILESINFO_TXT,
36  MAX_WORKER_NUMBER,
37  MODULES_ABC,
38  MODULES_CACHE,
39  NPM_ENTRIES_PROTO_BIN,
40  NPMENTRIES_TXT,
41  OH_MODULES,
42  PACKAGES,
43  PROTO_FILESINFO_TXT,
44  PROTOS,
45  red,
46  reset,
47  SOURCEMAPS,
48  SOURCEMAPS_JSON,
49  WIDGETS_ABC,
50  TS2ABC,
51  ES2ABC
52} from '../common/ark_define';
53import {
54  needAotCompiler,
55  isMasterOrPrimary,
56  isAotMode
57} from '../utils';
58import { CommonMode } from '../common/common_mode';
59import { newSourceMaps } from '../transform';
60import {
61  changeFileExtension,
62  getEs2abcFileThreadNumber,
63  isCommonJsPluginVirtualFile,
64  isCurrentProjectFiles,
65  shouldETSOrTSFileTransformToJS
66} from '../utils';
67import {
68  isPackageModulesFile,
69  mkdirsSync,
70  toUnixPath,
71  toHashData,
72  validateFilePathLength
73} from '../../../utils';
74import {
75  getPackageInfo,
76  getOhmUrlByFilepath,
77  isTs2Abc,
78  isEs2Abc
79} from '../../../ark_utils';
80import {
81  generateAot,
82  generateBuiltinAbc,
83  FaultHandler
84} from '../../../gen_aot';
85
86export class ModuleInfo {
87  filePath: string;
88  cacheFilePath: string;
89  recordName: string;
90  isCommonJs: boolean;
91  sourceFile: string;
92  packageName: string;
93
94  constructor(filePath: string, cacheFilePath: string, isCommonJs: boolean, recordName: string, sourceFile: string,
95    packageName: string
96  ) {
97    this.filePath = filePath;
98    this.cacheFilePath = cacheFilePath;
99    this.recordName = recordName;
100    this.isCommonJs = isCommonJs;
101    this.sourceFile = sourceFile;
102    this.packageName = packageName;
103  }
104}
105
106export class PackageEntryInfo {
107  pkgEntryPath: string;
108  pkgBuildPath: string;
109  constructor(pkgEntryPath: string, pkgBuildPath: string) {
110    this.pkgEntryPath = pkgEntryPath;
111    this.pkgBuildPath = pkgBuildPath;
112  }
113}
114
115export class ModuleMode extends CommonMode {
116  moduleInfos: Map<String, ModuleInfo>;
117  pkgEntryInfos: Map<String, PackageEntryInfo>;
118  hashJsonObject: any;
119  cacheSourceMapObject: any;
120  filesInfoPath: string;
121  npmEntriesInfoPath: string;
122  moduleAbcPath: string;
123  sourceMapPath: string;
124  cacheFilePath: string;
125  cacheSourceMapPath: string;
126  workerNumber: number;
127  npmEntriesProtoFilePath: string;
128  protoFilePath: string;
129  filterModuleInfos: Map<String, ModuleInfo>;
130  symlinkMap: any;
131
132  constructor(rollupObject: any) {
133    super(rollupObject);
134    this.moduleInfos = new Map<String, ModuleInfo>();
135    this.pkgEntryInfos = new Map<String, PackageEntryInfo>();
136    this.hashJsonObject = {};
137    this.cacheSourceMapObject = {};
138    this.filesInfoPath = path.join(this.projectConfig.cachePath, FILESINFO_TXT);
139    this.npmEntriesInfoPath = path.join(this.projectConfig.cachePath, NPMENTRIES_TXT);
140    const outPutABC: string = this.projectConfig.widgetCompile ? WIDGETS_ABC : MODULES_ABC;
141    this.moduleAbcPath = path.join(this.projectConfig.aceModuleBuild, outPutABC);
142    this.sourceMapPath = this.arkConfig.isDebug ? path.join(this.projectConfig.aceModuleBuild, SOURCEMAPS) :
143      path.join(this.projectConfig.cachePath, SOURCEMAPS);
144    this.cacheFilePath = path.join(this.projectConfig.cachePath, MODULES_CACHE);
145    this.cacheSourceMapPath = path.join(this.projectConfig.cachePath, SOURCEMAPS_JSON);
146    this.workerNumber = MAX_WORKER_NUMBER;
147    this.npmEntriesProtoFilePath = path.join(this.projectConfig.cachePath, PROTOS, NPM_ENTRIES_PROTO_BIN);
148    this.protoFilePath = path.join(this.projectConfig.cachePath, PROTOS, PROTO_FILESINFO_TXT);
149    this.hashJsonObject = {};
150    this.filterModuleInfos = new Map<String, ModuleInfo>();
151    this.symlinkMap = rollupObject.share.symlinkMap;
152  }
153
154  prepareForCompilation(rollupObject: any): void {
155    this.collectModuleFileList(rollupObject, rollupObject.getModuleIds());
156    this.removeCacheInfo(rollupObject);
157  }
158
159  collectModuleFileList(module: any, fileList: IterableIterator<string>) {
160    let moduleInfos: Map<String, ModuleInfo> = new Map<String, ModuleInfo>();
161    let pkgEntryInfos: Map<String, PackageEntryInfo> = new Map<String, PackageEntryInfo>();
162    for (const moduleId of fileList) {
163      if (isCommonJsPluginVirtualFile(moduleId) || !isCurrentProjectFiles(moduleId, this.projectConfig)) {
164        continue;
165      }
166      const moduleInfo: any = module.getModuleInfo(moduleId);
167      if (moduleInfo['meta']['isNodeEntryFile']) {
168        this.getPackageEntryInfo(moduleId, moduleInfo['meta'], pkgEntryInfos);
169      }
170
171      this.processModuleInfos(moduleId, moduleInfos, moduleInfo['meta']);
172    }
173    this.moduleInfos = moduleInfos;
174    this.pkgEntryInfos = pkgEntryInfos;
175  }
176
177  private getPackageEntryInfo(filePath: string, metaInfo: any, pkgEntryInfos: Map<String, PackageEntryInfo>) {
178    if (metaInfo['isLocalDependency']) {
179      const hostModulesInfo: any = metaInfo['hostModulesInfo'];
180      const pkgBuildPath: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, metaInfo['moduleName']);
181      hostModulesInfo.forEach(hostModuleInfo => {
182        const hostDependencyName: string = hostModuleInfo['hostDependencyName'];
183        const hostModuleName: string = hostModuleInfo['hostModuleName'];
184        const pkgEntryPath: string = toUnixPath(path.join(`${PACKAGES}@${hostModuleName}`, hostDependencyName));
185        if (!pkgEntryInfos.has(pkgEntryPath)) {
186          pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath));
187        }
188      });
189      return;
190    }
191
192    if (!metaInfo['pkgPath']) {
193      this.logger.debug("Failed to get 'pkgPath' from metaInfo. File: ", filePath);
194      return;
195    }
196    const pkgPath: string = metaInfo['pkgPath'];
197    let originPkgEntryPath: string = toUnixPath(filePath.replace(pkgPath, ''));
198    if (originPkgEntryPath.startsWith('/')) {
199      originPkgEntryPath = originPkgEntryPath.slice(1, originPkgEntryPath.length);
200    }
201    const pkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(pkgPath));
202    let pkgBuildPath: string = path.join(pkgEntryPath, originPkgEntryPath);
203    pkgBuildPath = toUnixPath(pkgBuildPath.substring(0, pkgBuildPath.lastIndexOf('.')));
204    if (!pkgEntryInfos.has(pkgEntryPath)) {
205      pkgEntryInfos.set(pkgEntryPath, new PackageEntryInfo(pkgEntryPath, pkgBuildPath));
206    }
207    // create symlink path to actual path mapping in ohpm
208    if (this.projectConfig.packageDir == OH_MODULES && this.symlinkMap) {
209      const symlinkEntries: any = Object.entries(this.symlinkMap);
210      for (const [actualPath, symlinkPaths] of symlinkEntries) {
211        if (actualPath === pkgPath) {
212          (<string[]>symlinkPaths).forEach((symlink: string) => {
213            const symlinkPkgEntryPath: string = toUnixPath(this.getPkgModulesFilePkgName(symlink));
214            if (!pkgEntryInfos.has(symlinkPkgEntryPath)) {
215              pkgEntryInfos.set(symlinkPkgEntryPath, new PackageEntryInfo(symlinkPkgEntryPath, pkgEntryPath));
216            }
217          });
218          break;
219        }
220      }
221    }
222  }
223
224  private processModuleInfos(moduleId: string, moduleInfos: Map<String, ModuleInfo>, metaInfo?: any) {
225    switch (path.extname(moduleId)) {
226      case EXTNAME_ETS: {
227        const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig) ? EXTNAME_JS : EXTNAME_TS;
228        this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos);
229        break;
230      }
231      case EXTNAME_TS: {
232        const extName: string = shouldETSOrTSFileTransformToJS(moduleId, this.projectConfig) ? EXTNAME_JS : '';
233        this.addModuleInfoItem(moduleId, false, extName, metaInfo, moduleInfos);
234        break;
235      }
236      case EXTNAME_JS:
237      case EXTNAME_MJS:
238      case EXTNAME_CJS: {
239        const extName: string = (moduleId.endsWith(EXTNAME_MJS) || moduleId.endsWith(EXTNAME_CJS)) ? EXTNAME_JS : '';
240        const isCommonJS: boolean = metaInfo && metaInfo['commonjs'] && metaInfo['commonjs']['isCommonJS'];
241        this.addModuleInfoItem(moduleId, isCommonJS, extName, metaInfo, moduleInfos);
242        break;
243      }
244      case EXTNAME_JSON: {
245        this.addModuleInfoItem(moduleId, false, '', metaInfo, moduleInfos);
246        break;
247      }
248      default:
249        break;
250    }
251  }
252
253  private addModuleInfoItem(filePath: string, isCommonJs: boolean, extName: string, metaInfo: any, moduleInfos: any) {
254    let namespace: string = metaInfo['moduleName'];
255    let recordName: string = getOhmUrlByFilepath(filePath, this.projectConfig, this.logger, namespace);
256    let sourceFile: string = filePath.replace(this.projectConfig.projectRootPath + path.sep, '');
257    let cacheFilePath: string =
258      this.genFileCachePath(filePath, this.projectConfig.projectRootPath, this.projectConfig.cachePath);
259    let packageName: string = '';
260    if (isPackageModulesFile(filePath, this.projectConfig)) {
261      packageName = this.getPkgModulesFilePkgName(metaInfo['pkgPath']);
262    } else {
263      packageName =
264        metaInfo['isLocalDependency'] ? namespace : getPackageInfo(this.projectConfig.aceModuleJsonPath)[1];
265    }
266
267    if (extName.length !== 0) {
268      cacheFilePath = changeFileExtension(cacheFilePath, extName);
269    }
270
271    cacheFilePath = toUnixPath(cacheFilePath);
272    recordName = toUnixPath(recordName);
273    sourceFile = toUnixPath(sourceFile);
274    packageName = toUnixPath(packageName);
275
276    moduleInfos.set(filePath, new ModuleInfo(filePath, cacheFilePath, isCommonJs, recordName, sourceFile, packageName));
277  }
278
279  updateCachedSourceMaps(): void {
280    if (!fs.existsSync(this.cacheSourceMapPath)) {
281      this.cacheSourceMapObject = newSourceMaps;
282      return;
283    }
284
285    this.cacheSourceMapObject = JSON.parse(fs.readFileSync(this.cacheSourceMapPath).toString());
286
287    // remove unused source files's sourceMap
288    let unusedFiles = [];
289    let compileFileList: Set<string> = new Set();
290    this.moduleInfos.forEach((moduleInfo: ModuleInfo, moduleId: string) => {
291      compileFileList.add(toUnixPath(moduleId));
292    })
293
294    Object.keys(this.cacheSourceMapObject).forEach(key => {
295      const sourceFileAbsolutePath: string = toUnixPath(path.join(this.projectConfig.projectRootPath, key));
296      if (!compileFileList.has(sourceFileAbsolutePath)) {
297        unusedFiles.push(key);
298      }
299    });
300    unusedFiles.forEach(file => {
301      delete this.cacheSourceMapObject[file];
302    })
303
304    // update sourceMap
305    Object.keys(newSourceMaps).forEach(key => {
306      this.cacheSourceMapObject[key] = newSourceMaps[key];
307    });
308  }
309
310  buildModuleSourceMapInfo() {
311    if (this.projectConfig.widgetCompile) {
312      return;
313    }
314
315    this.updateCachedSourceMaps();
316    this.triggerAsync(() => {
317      fs.writeFile(this.sourceMapPath, JSON.stringify(this.cacheSourceMapObject, null, 2), 'utf-8', (err) => {
318        if (err) {
319          this.throwArkTsCompilerError('ArkTS:ERROR failed to write sourceMaps');
320        }
321        fs.copyFileSync(this.sourceMapPath, this.cacheSourceMapPath);
322        this.triggerEndSignal();
323      });
324    });
325  }
326
327  generateEs2AbcCmd() {
328    const fileThreads = getEs2abcFileThreadNumber();
329    this.cmdArgs.push(`"@${this.filesInfoPath}"`);
330    this.cmdArgs.push('--npm-module-entry-list');
331    this.cmdArgs.push(`"${this.npmEntriesInfoPath}"`);
332    this.cmdArgs.push('--output');
333    this.cmdArgs.push(`"${this.moduleAbcPath}"`);
334    this.cmdArgs.push('--file-threads');
335    this.cmdArgs.push(`"${fileThreads}"`);
336    this.cmdArgs.push('--merge-abc');
337    if (isAotMode(this.projectConfig) && isEs2Abc(this.projectConfig)) {
338      this.cmdArgs.push("--type-extractor");
339    }
340  }
341
342  addCacheFileArgs() {
343    this.cmdArgs.push('--cache-file');
344    this.cmdArgs.push(`"@${this.cacheFilePath}"`);
345  }
346
347  private generateCompileFilesInfo() {
348    let filesInfo: string = '';
349    this.moduleInfos.forEach((info) => {
350      const moduleType: string = info.isCommonJs ? COMMONJS : ESM;
351      filesInfo += `${info.cacheFilePath};${info.recordName};${moduleType};${info.sourceFile};${info.packageName}\n`;
352    });
353    fs.writeFileSync(this.filesInfoPath, filesInfo, 'utf-8');
354  }
355
356  private generateNpmEntriesInfo() {
357    let entriesInfo: string = '';
358    for (const value of this.pkgEntryInfos.values()) {
359      entriesInfo += `${value.pkgEntryPath}:${value.pkgBuildPath}\n`;
360    }
361    fs.writeFileSync(this.npmEntriesInfoPath, entriesInfo, 'utf-8');
362  }
363
364  private generateAbcCacheFilesInfo(): void {
365    let abcCacheFilesInfo: string = '';
366
367    // generate source file cache
368    this.moduleInfos.forEach((info) => {
369      let abcCacheFilePath: string = changeFileExtension(info.cacheFilePath, EXTNAME_PROTO_BIN);
370      abcCacheFilesInfo += `${info.cacheFilePath};${abcCacheFilePath}\n`;
371    });
372
373    // generate npm entries cache
374    let npmEntriesCacheFilePath: string = changeFileExtension(this.npmEntriesInfoPath, EXTNAME_PROTO_BIN);
375    abcCacheFilesInfo += `${this.npmEntriesInfoPath};${npmEntriesCacheFilePath}\n`;
376
377    fs.writeFileSync(this.cacheFilePath, abcCacheFilesInfo, 'utf-8');
378  }
379
380  private genDescriptionsForMergedEs2abc() {
381    this.generateCompileFilesInfo();
382    this.generateNpmEntriesInfo();
383    this.generateAbcCacheFilesInfo();
384  }
385
386  generateMergedAbcOfEs2Abc() {
387    // collect data error from subprocess
388    let errMsg: string = '';
389    this.genDescriptionsForMergedEs2abc();
390    const genAbcCmd: string = this.cmdArgs.join(' ');
391    try {
392      const child = this.triggerAsync(() => {
393        return childProcess.exec(genAbcCmd, { windowsHide: true });
394      });
395      child.on('exit', (code: any) => {
396        if (code === FAIL) {
397          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc');
398        }
399        this.triggerEndSignal();
400        this.processAotIfNeeded();
401      });
402
403      child.on('error', (err: any) => {
404        this.throwArkTsCompilerError(err.toString());
405      });
406
407      child.stderr.on('data', (data: any) => {
408        errMsg += data.toString();
409      });
410
411      child.stderr.on('end', (data: any) => {
412        if (errMsg !== undefined && errMsg.length > 0) {
413          this.logger.error(red, errMsg, reset);
414        }
415      });
416    } catch (e) {
417      this.throwArkTsCompilerError('ArkTS:ERROR failed to execute es2abc. Error message: ' + e.toString());
418    }
419  }
420
421  filterModulesByHashJson() {
422    if (this.hashJsonFilePath.length === 0 || !fs.existsSync(this.hashJsonFilePath)) {
423      for (const key of this.moduleInfos.keys()) {
424        this.filterModuleInfos.set(key, this.moduleInfos.get(key));
425      }
426      return;
427    }
428
429    let updatedJsonObject: any = {};
430    let jsonObject: any = {};
431    let jsonFile: string = '';
432
433    if (fs.existsSync(this.hashJsonFilePath)) {
434      jsonFile = fs.readFileSync(this.hashJsonFilePath).toString();
435      jsonObject = JSON.parse(jsonFile);
436      this.filterModuleInfos = new Map<string, ModuleInfo>();
437      for (const [key, value] of this.moduleInfos) {
438        const cacheFilePath: string = value.cacheFilePath;
439        const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
440        if (!fs.existsSync(cacheFilePath)) {
441          this.throwArkTsCompilerError(`ArkTS:ERROR ${cacheFilePath} is lost`);
442        }
443        if (fs.existsSync(cacheProtoFilePath)) {
444          const hashCacheFileContentData: any = toHashData(cacheFilePath);
445          const hashProtoFileContentData: any = toHashData(cacheProtoFilePath);
446          if (jsonObject[cacheFilePath] === hashCacheFileContentData &&
447            jsonObject[cacheProtoFilePath] === hashProtoFileContentData) {
448            updatedJsonObject[cacheFilePath] = cacheFilePath;
449            updatedJsonObject[cacheProtoFilePath] = cacheProtoFilePath;
450            continue;
451          }
452        }
453        this.filterModuleInfos.set(key, value);
454      }
455    }
456
457    this.hashJsonObject = updatedJsonObject;
458  }
459
460  getSplittedModulesByNumber() {
461    const result: any = [];
462    if (this.filterModuleInfos.size < this.workerNumber) {
463      for (const value of this.filterModuleInfos.values()) {
464        result.push([value]);
465      }
466      return result;
467    }
468
469    for (let i = 0; i < this.workerNumber; ++i) {
470      result.push([]);
471    }
472
473    let pos: number = 0;
474    for (const value of this.filterModuleInfos.values()) {
475      const chunk = pos % this.workerNumber;
476      result[chunk].push(value);
477      pos++;
478    }
479
480    return result;
481  }
482
483  invokeTs2AbcWorkersToGenProto(splittedModules) {
484    let ts2abcCmdArgs: string[] = this.cmdArgs.slice(0);
485    ts2abcCmdArgs.push('--output-proto');
486    ts2abcCmdArgs.push('--merge-abc');
487    ts2abcCmdArgs.push('--input-file');
488    if (isMasterOrPrimary()) {
489      this.setupCluster(cluster);
490      this.workerNumber = splittedModules.length;
491      for (let i = 0; i < this.workerNumber; ++i) {
492        const sn: number = i + 1;
493        const workerFileName: string = `${FILESINFO}_${sn}${EXTNAME_TXT}`;
494        const workerData: any = {
495          inputs: JSON.stringify(splittedModules[i]),
496          cmd: ts2abcCmdArgs.join(' '),
497          workerFileName: workerFileName,
498          mode: ESMODULE,
499          cachePath: this.projectConfig.cachePath
500        };
501        this.triggerAsync(() => {
502          const worker: any = cluster.fork(workerData);
503          worker.on('message', (errorMsg) => {
504            this.logger.error(red, errorMsg.data.toString(), reset);
505            this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
506          });
507        });
508      }
509    }
510  }
511
512  processTs2abcWorkersToGenAbc() {
513    this.generateNpmEntriesInfo();
514    let workerCount: number = 0;
515    if (isMasterOrPrimary()) {
516      cluster.on('exit', (worker, code, signal) => {
517        if (code === FAIL) {
518          this.throwArkTsCompilerError('ArkTS:ERROR failed to execute ts2abc');
519        }
520        workerCount++;
521        if (workerCount === this.workerNumber) {
522          this.generateNpmEntryToGenProto();
523          this.generateProtoFilesInfo();
524          this.mergeProtoToAbc();
525          this.processAotIfNeeded();
526          this.afterCompilationProcess();
527        }
528        this.triggerEndSignal();
529      });
530      if (this.workerNumber == 0) {
531        // process aot for no source file changed.
532        this.processAotIfNeeded();
533      }
534    }
535  }
536
537  private processAotIfNeeded(): void {
538    if (!needAotCompiler(this.projectConfig)) {
539      return;
540    }
541    let faultHandler: FaultHandler = ((error: string) => { this.throwArkTsCompilerError(error); })
542    const builtinAbcPath: string = generateBuiltinAbc(this.arkConfig.arkRootPath, this.initCmdEnv(),
543      this.projectConfig.cachePath, this.logger, faultHandler, this.projectConfig.pandaMode);
544    generateAot(this.arkConfig.arkRootPath, builtinAbcPath, this.moduleAbcPath, this.projectConfig, this.logger, faultHandler);
545  }
546
547  private genFileCachePath(filePath: string, projectRootPath: string, cachePath: string): string {
548    const sufStr: string = toUnixPath(filePath).replace(toUnixPath(projectRootPath), '');
549    const output: string = path.join(cachePath, sufStr);
550    return output;
551  }
552
553  private getPkgModulesFilePkgName(pkgPath: string) {
554    pkgPath = toUnixPath(pkgPath);
555    const packageDir: string = this.projectConfig.packageDir;
556    const projectRootPath = toUnixPath(this.projectConfig.projectRootPath);
557    const projectPkgModulesPath: string = toUnixPath(path.join(projectRootPath, packageDir));
558    let pkgName: string = '';
559    if (pkgPath.includes(projectPkgModulesPath)) {
560      pkgName = path.join(PACKAGES, pkgPath.replace(projectPkgModulesPath, ''));
561    } else {
562      for (const key in this.projectConfig.modulePathMap) {
563        const value: string = this.projectConfig.modulePathMap[key];
564        const fakeModulePkgModulesPath: string = toUnixPath(path.resolve(value, packageDir));
565        if (pkgPath.indexOf(fakeModulePkgModulesPath) !== -1) {
566          const tempFilePath: string = pkgPath.replace(projectRootPath, '');
567          pkgName = path.join(`${PACKAGES}@${key}`,
568            tempFilePath.substring(tempFilePath.indexOf(packageDir) + packageDir.length + 1));
569          break;
570        }
571      }
572    }
573
574    return pkgName.replace(new RegExp(packageDir, 'g'), PACKAGES);
575  }
576
577  private generateProtoFilesInfo() {
578    validateFilePathLength(this.protoFilePath, this.logger);
579    mkdirsSync(path.dirname(this.protoFilePath));
580    let protoFilesInfo: string = '';
581    const sortModuleInfos: any = new Map([...this.moduleInfos].sort());
582    for (const value of sortModuleInfos.values()) {
583      const cacheProtoPath: string = changeFileExtension(value.cacheFilePath, EXTNAME_PROTO_BIN);
584      protoFilesInfo += `${toUnixPath(cacheProtoPath)}\n`;
585    }
586    if (this.pkgEntryInfos.size > 0) {
587      protoFilesInfo += `${toUnixPath(this.npmEntriesProtoFilePath)}\n`;
588    }
589    fs.writeFileSync(this.protoFilePath, protoFilesInfo, 'utf-8');
590  }
591
592  private mergeProtoToAbc() {
593    mkdirsSync(this.projectConfig.aceModuleBuild);
594    const cmd: any = `"${this.arkConfig.mergeAbcPath}" --input "@${this.protoFilePath}" --outputFilePath "${
595      this.projectConfig.aceModuleBuild}" --output ${MODULES_ABC} --suffix protoBin`;
596    try {
597      childProcess.execSync(cmd, { windowsHide: true });
598    } catch (e) {
599      this.throwArkTsCompilerError(`ArkTS:ERROR failed to merge proto file to abc, error message:` + e.toString());
600    }
601  }
602
603  private afterCompilationProcess() {
604    this.writeHashJson();
605  }
606
607  private writeHashJson() {
608    if (this.hashJsonFilePath.length === 0) {
609      return;
610    }
611
612    for (const value of this.filterModuleInfos.values()) {
613      const cacheFilePath: string = value.cacheFilePath;
614      const cacheProtoFilePath: string = changeFileExtension(cacheFilePath, EXTNAME_PROTO_BIN);
615      if (!fs.existsSync(cacheFilePath) || !fs.existsSync(cacheProtoFilePath)) {
616        this.throwArkTsCompilerError(
617          `ArkTS:ERROR ${cacheFilePath} or  ${cacheProtoFilePath} is lost`
618        );
619      }
620      const hashCacheFileContentData: any = toHashData(cacheFilePath);
621      const hashCacheProtoContentData: any = toHashData(cacheProtoFilePath);
622      this.hashJsonObject[cacheFilePath] = hashCacheFileContentData;
623      this.hashJsonObject[cacheProtoFilePath] = hashCacheProtoContentData;
624    }
625
626    fs.writeFileSync(this.hashJsonFilePath, JSON.stringify(this.hashJsonObject));
627  }
628
629  private generateNpmEntryToGenProto() {
630    if (this.pkgEntryInfos.size <= 0) {
631      return;
632    }
633    mkdirsSync(path.dirname(this.npmEntriesProtoFilePath));
634    const cmd: string = `"${this.arkConfig.js2abcPath}" --compile-npm-entries "${
635      this.npmEntriesInfoPath}" "${this.npmEntriesProtoFilePath}"`;
636    try {
637      childProcess.execSync(cmd, { windowsHide: true });
638    } catch (e) {
639      this.throwArkTsCompilerError(`ArkTS:ERROR failed to generate npm proto file to abc. Error message: ` + e.toString());
640    }
641  }
642
643  private removeCompilationCache(): void {
644    if (isEs2Abc(this.projectConfig)) {
645      this.removeEs2abcCompilationCache();
646    } else if (isTs2Abc(this.projectConfig)) {
647      this.removeTs2abcCompilationCache();
648    } else {
649      this.throwArkTsCompilerError(`Invalid projectConfig.pandaMode for module build, should be either
650      "${TS2ABC}" or "${ES2ABC}"`);
651    }
652  }
653
654  private removeEs2abcCompilationCache(): void {
655    if (fs.existsSync(this.cacheFilePath)) {
656      const data: string = fs.readFileSync(this.cacheFilePath, 'utf-8');
657      const lines: any = data.split(/\r?\n/);
658      lines.forEach(line => {
659        const [, abcCacheFilePath]: any = line.split(';');
660        if (fs.existsSync(abcCacheFilePath)) {
661          fs.unlinkSync(abcCacheFilePath);
662        }
663      });
664      fs.unlinkSync(this.cacheFilePath);
665    }
666  }
667
668  private removeTs2abcCompilationCache(): void {
669    if (fs.existsSync(this.hashJsonFilePath)) {
670      fs.unlinkSync(this.hashJsonFilePath);
671    }
672    if (fs.existsSync(this.protoFilePath)) {
673      const data: string = fs.readFileSync(this.protoFilePath, 'utf-8');
674      const lines: any = data.split(/\r?\n/);
675      lines.forEach(line => {
676        const protoFilePath: string = line;
677        if (fs.existsSync(protoFilePath)) {
678          fs.unlinkSync(protoFilePath);
679        }
680      });
681      fs.unlinkSync(this.protoFilePath);
682    }
683  }
684}
685