• 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
16const fs = require('fs');
17const path = require('path');
18const cluster = require('cluster');
19const process = require('process');
20const crypto = require('crypto');
21const events = require('events');
22const os = require('os');
23const childProcess = require('child_process');
24
25const forward = '(global.___mainEntry___ = function (globalObjects) {' + '\n' +
26              '  var define = globalObjects.define;' + '\n' +
27              '  var require = globalObjects.require;' + '\n' +
28              '  var bootstrap = globalObjects.bootstrap;' + '\n' +
29              '  var register = globalObjects.register;' + '\n' +
30              '  var render = globalObjects.render;' + '\n' +
31              '  var $app_define$ = globalObjects.$app_define$;' + '\n' +
32              '  var $app_bootstrap$ = globalObjects.$app_bootstrap$;' + '\n' +
33              '  var $app_require$ = globalObjects.$app_require$;' + '\n' +
34              '  var history = globalObjects.history;' + '\n' +
35              '  var Image = globalObjects.Image;' + '\n' +
36              '  var OffscreenCanvas = globalObjects.OffscreenCanvas;' + '\n' +
37              '  (function(global) {' + '\n' +
38              '    "use strict";' + '\n';
39const last = '\n' + '})(this.__appProto__);' + '\n' + '})';
40const genAbcScript = 'gen-abc.js';
41let output;
42let isWin = false;
43let isMac = false;
44let isDebug = false;
45let arkDir;
46let nodeJs;
47let intermediateJsBundle = [];
48let workerFile = null;
49let fileterIntermediateJsBundle = [];
50let hashJsonObject = {};
51let buildPathInfo = "";
52const SUCCESS = 0;
53const FAIL = 1;
54const red = '\u001b[31m';
55const reset = '\u001b[39m';
56const blue = '\u001b[34m';
57const hashFile = 'gen_hash.json';
58const ARK = '/ark/';
59const NODE_MODULES = 'node_modules';
60const TEMPORARY = 'temporary';
61const TS2ABC = 'ts2abc';
62const ES2ABC = 'es2abc';
63const WINDOWS = 'Windows_NT';
64const LINUX = 'Linux';
65const OPENHARMONY = 'openharmony';
66const MAC = 'Darwin';
67const FILESINFO_TXT = 'filesInfo.txt';
68const manageBunldeWorkersScript = 'manage-bundle-workers.js';
69const PREBUILDINFO_JSON = 'preBuildInfo.json';
70
71class GenAbcPlugin {
72  constructor(output_, arkDir_, nodeJs_, workerFile_, isDebug_) {
73    output = output_;
74    arkDir = arkDir_;
75    nodeJs = nodeJs_;
76    isDebug = isDebug_;
77    workerFile = workerFile_;
78  }
79  apply(compiler) {
80    if (fs.existsSync(path.resolve(arkDir, 'build-win'))) {
81      isWin = true;
82    } else if (fs.existsSync(path.resolve(arkDir, 'build-mac'))) {
83      isMac = true;
84    } else if (!fs.existsSync(path.resolve(arkDir, 'build'))) {
85      console.error(red, 'ERROR find build fail', reset);
86      process.exitCode = FAIL;
87      return;
88    }
89
90    if (!checkNodeModules()) {
91      process.exitCode = FAIL;
92      return;
93    }
94
95    // for preview mode max listeners
96    events.EventEmitter.defaultMaxListeners = 100;
97
98    compiler.hooks.emit.tap('GenAbcPlugin', (compilation) => {
99      const assets = compilation.assets;
100      const keys = Object.keys(assets);
101      buildPathInfo = output;
102      keys.forEach(key => {
103        // choice *.js
104        if (output && path.extname(key) === '.js') {
105          let newContent = assets[key].source();
106          if (checkWorksFile(key, workerFile) && key !== 'commons.js' && key !== 'vendors.js') {
107            newContent = forward + newContent + last;
108          }
109          if (key === 'commons.js' || key === 'vendors.js' || !checkWorksFile(key, workerFile)) {
110            newContent = `\n\n\n\n\n\n\n\n\n\n\n\n\n\n` + newContent;
111          }
112          const keyPath = key.replace(/\.js$/, ".temp.js");
113          writeFileSync(newContent, output, keyPath, key, true);
114        } else if (output && path.extname(key) === '.json' &&
115          process.env.DEVICE_LEVEL === 'card' && process.env.configOutput && !checkI18n(key)) {
116          writeFileSync(assets[key].source(), output, key, key, false);
117        }
118      })
119    });
120    compiler.hooks.afterEmit.tap('GenAbcPluginMultiThread', () => {
121      if (intermediateJsBundle.length === 0) {
122        return;
123      }
124      buildPathInfo = output;
125      clearWebpackCacheByBuildInfo();
126      setPrebuildInfo();
127      processMultiThreadEntry();
128    });
129  }
130}
131
132function processMultiThreadEntry() {
133  if (isTs2Abc() || process.env.minPlatformVersion === "8") {
134      invokeWorkerToGenAbc();
135  } else if (isEs2Abc()) {
136    generateAbcByEs2AbcOfBundleMode(intermediateJsBundle);
137  } else {
138    console.debug(red, `ERROR please set panda module`, reset);
139  }
140}
141
142function checkI18n(key) {
143  const outI18nPath = path.resolve(process.env.configOutput, key);
144  const projectI18nPath = outI18nPath.replace(output, process.env.projectPath);
145  if (projectI18nPath.indexOf(
146    path.resolve(__dirname, process.env.projectPath, 'i18n')) > -1) {
147    return true;
148  }
149  return false;
150}
151
152function checkWorksFile(assetPath, workerFile) {
153  if (workerFile === null) {
154    if (assetPath.search("./workers/") !== 0) {
155      return true;
156    } else {
157      return false;
158    }
159  } else {
160    for (const key in workerFile) {
161      let keyExt = key + '.js';
162      if (keyExt === assetPath) {
163        return false;
164      }
165    }
166  }
167
168  return true;
169}
170
171function writeFileSync(inputString, buildPath, keyPath, jsBundleFile, isToBin) {
172    let output = path.resolve(buildPath, keyPath);
173    validateFilePathLength(output);
174    let parent = path.join(output, '..');
175    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
176        mkDir(parent);
177    }
178    if (!isToBin) {
179      fs.writeFileSync(output, inputString);
180      return;
181    }
182    let cacheOutputPath = "";
183    if (process.env.cachePath) {
184      let buildDirArr = buildPathInfo.split(path.sep);
185      let abilityDir = buildDirArr[buildDirArr.length - 1];
186      cacheOutputPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, keyPath);
187    } else {
188      cacheOutputPath = output;
189    }
190    validateFilePathLength(cacheOutputPath);
191    parent = path.join(cacheOutputPath, '..');
192    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
193      mkDir(parent);
194    }
195    fs.writeFileSync(cacheOutputPath, inputString);
196    if (fs.existsSync(cacheOutputPath)) {
197      let fileSize = fs.statSync(cacheOutputPath).size;
198      let sourceFile = output.replace(/\.temp\.js$/, "_.js");
199      if (!isDebug && (process.env.aceBuildJson && fs.existsSync(process.env.aceBuildJson))) {
200        const buildJsonInfo = JSON.parse(fs.readFileSync(process.env.aceBuildJson).toString());
201        sourceFile = toUnixPath(sourceFile.replace(buildJsonInfo.projectRootPath + path.sep, ''));
202      } else {
203        sourceFile = toUnixPath(sourceFile.replace(process.env.projectRootPath + path.sep, ''));
204      }
205      output = toUnixPath(output);
206      cacheOutputPath = toUnixPath(cacheOutputPath);
207      intermediateJsBundle.push({
208        path: output, size: fileSize, cacheOutputPath: cacheOutputPath, sourceFile: sourceFile
209      });
210    } else {
211      console.debug(red, `ERROR Failed to convert file ${jsBundleFile} to bin. ${output} is lost`, reset);
212      process.exitCode = FAIL;
213    }
214}
215
216function mkDir(path_) {
217    const parent = path.join(path_, '..');
218    if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
219        mkDir(parent);
220    }
221    fs.mkdirSync(path_);
222}
223
224function getSmallestSizeGroup(groupSize) {
225  let groupSizeArray = Array.from(groupSize);
226  groupSizeArray.sort(function(g1, g2) {
227    return g1[1] - g2[1]; // sort by size
228  });
229  return groupSizeArray[0][0];
230}
231
232function splitJsBundlesBySize(bundleArray, groupNumber) {
233  let result = [];
234  if (bundleArray.length < groupNumber) {
235    for (let value of bundleArray) {
236      result.push([value]);
237    }
238    return result;
239  }
240
241  bundleArray.sort(function(f1, f2) {
242    return f2.size - f1.size;
243  });
244  let groupFileSize = new Map();
245  for (let i = 0; i < groupNumber; ++i) {
246    result.push([]);
247    groupFileSize.set(i, 0);
248  }
249
250  let index = 0;
251  while(index < bundleArray.length) {
252    let smallestGroup = getSmallestSizeGroup(groupFileSize);
253    result[smallestGroup].push(bundleArray[index]);
254    let sizeUpdate = groupFileSize.get(smallestGroup) + bundleArray[index].size;
255    groupFileSize.set(smallestGroup, sizeUpdate);
256    index++;
257  }
258  return result;
259}
260
261function invokeWorkerToGenAbc() {
262  if (process.env.isPreview === "true") {
263    process.exitCode = SUCCESS;
264  }
265  let maxWorkerNumber = isEs2Abc() ? os.cpus().length : 3;
266  const abcArgs = initAbcEnv();
267  let cmdPrefix = initCmdPrefix(abcArgs);
268
269  filterIntermediateJsBundleByHashJson(buildPathInfo, intermediateJsBundle);
270  const splitedBundles = splitJsBundlesBySize(fileterIntermediateJsBundle, maxWorkerNumber);
271  const workerNumber = maxWorkerNumber < splitedBundles.length ? maxWorkerNumber : splitedBundles.length;
272
273  try  {
274    if (process.env.isPreview === 'true') {
275      processWorkersOfPreviewMode(splitedBundles, cmdPrefix, workerNumber);
276    } else {
277      processWorkersOfBuildMode(splitedBundles, cmdPrefix, workerNumber);
278    }
279  } catch (e) {
280    console.debug(red, `ERROR failed to generate abc. Error message: ${e} `, reset);
281    process.env.abcCompileSuccess = 'false';
282    if (process.env.isPreview !== 'true') {
283      process.exit(FAIL);
284    }
285  }
286}
287
288function clearGlobalInfo() {
289  if (process.env.isPreview !== "true") {
290    intermediateJsBundle = [];
291  }
292  fileterIntermediateJsBundle = [];
293  hashJsonObject = {};
294}
295
296function filterIntermediateJsBundleByHashJson(buildPath, inputPaths) {
297  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
298
299  for (let i = 0; i < inputPaths.length; ++i) {
300    fileterIntermediateJsBundle.push(inputPaths[i]);
301  }
302  const hashFilePath = genHashJsonPath(buildPath);
303  if (hashFilePath.length == 0) {
304    return;
305  }
306  let updateJsonObject = {};
307  let jsonObject = {};
308  let jsonFile = "";
309  if (fs.existsSync(hashFilePath)) {
310    jsonFile = fs.readFileSync(hashFilePath).toString();
311    jsonObject = JSON.parse(jsonFile);
312    fileterIntermediateJsBundle = [];
313    for (let i = 0; i < inputPaths.length; ++i) {
314      const cacheOutputPath = inputPaths[i].cacheOutputPath;
315      const cacheAbcFilePath = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
316      if (!fs.existsSync(cacheOutputPath)) {
317        console.debug(red, `ERROR ${cacheOutputPath} is lost`, reset);
318        process.exitCode = FAIL;
319        break;
320      }
321
322      if (fs.existsSync(cacheAbcFilePath)) {
323        const hashInputContentData = toHashData(cacheOutputPath);
324        const hashAbcContentData = toHashData(cacheAbcFilePath);
325        if (jsonObject[cacheOutputPath] === hashInputContentData && jsonObject[cacheAbcFilePath] === hashAbcContentData) {
326          updateJsonObject[cacheOutputPath] = hashInputContentData;
327          updateJsonObject[cacheAbcFilePath] = hashAbcContentData;
328        } else {
329          fileterIntermediateJsBundle.push(inputPaths[i]);
330        }
331      } else {
332        fileterIntermediateJsBundle.push(inputPaths[i]);
333      }
334    }
335  }
336
337  hashJsonObject = updateJsonObject;
338}
339
340function writeHashJson() {
341  for (let i = 0; i < fileterIntermediateJsBundle.length; ++i) {
342    const cacheOutputPath = fileterIntermediateJsBundle[i].cacheOutputPath;
343    const cacheAbcFilePath = cacheOutputPath.replace(/\.temp\.js$/, '.abc');
344    if (!fs.existsSync(cacheOutputPath) || !fs.existsSync(cacheAbcFilePath)) {
345      console.debug(red, `ERROR ${cacheOutputPath} is lost`, reset);
346      process.exitCode = FAIL;
347      break;
348    }
349    const hashInputContentData = toHashData(cacheOutputPath);
350    const hashAbcContentData = toHashData(cacheAbcFilePath);
351    hashJsonObject[cacheOutputPath] = hashInputContentData;
352    hashJsonObject[cacheAbcFilePath] = hashAbcContentData;
353  }
354  const hashFilePath = genHashJsonPath(buildPathInfo);
355  if (hashFilePath.length == 0) {
356    return;
357  }
358  fs.writeFileSync(hashFilePath, JSON.stringify(hashJsonObject));
359}
360
361function genHashJsonPath(buildPath) {
362  buildPath = toUnixPath(buildPath);
363  if (process.env.cachePath) {
364    if (!fs.existsSync(process.env.cachePath) || !fs.statSync(process.env.cachePath).isDirectory()) {
365      return '';
366    }
367    let buildDirArr = buildPathInfo.split(path.sep);
368    let abilityDir = buildDirArr[buildDirArr.length - 1];
369    let hashJsonPath = path.join(process.env.cachePath, TEMPORARY, abilityDir, hashFile);
370    validateFilePathLength(hashJsonPath);
371    let parent = path.join(hashJsonPath, '..');
372    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
373      mkDir(parent);
374    }
375    return hashJsonPath;
376  } else if (buildPath.indexOf(ARK) >= 0) {
377    const dataTmps = buildPath.split(ARK);
378    const hashPath = path.join(dataTmps[0], ARK);
379    if (!fs.existsSync(hashPath) || !fs.statSync(hashPath).isDirectory()) {
380      return '';
381    }
382    let hashJsonPath = path.join(hashPath, hashFile);
383    validateFilePathLength(hashJsonPath);
384    return hashJsonPath;
385  } else {
386    return '';
387  }
388}
389
390function toUnixPath(data) {
391  if (/^win/.test(require('os').platform())) {
392    const fileTmps = data.split(path.sep);
393    const newData = path.posix.join(...fileTmps);
394    return newData;
395  }
396  return data;
397}
398
399function toHashData(path) {
400  const content = fs.readFileSync(path);
401  const hash = crypto.createHash('sha256');
402  hash.update(content);
403  return hash.digest('hex');
404}
405
406module.exports = {
407  GenAbcPlugin: GenAbcPlugin,
408  checkWorksFile: checkWorksFile
409}
410
411function copyFileCachePathToBuildPath() {
412  for (let i = 0; i < intermediateJsBundle.length; ++i) {
413    const abcFile = intermediateJsBundle[i].path.replace(/\.temp\.js$/, ".abc");
414    const cacheOutputPath = intermediateJsBundle[i].cacheOutputPath;
415    const cacheAbcFilePath = intermediateJsBundle[i].cacheOutputPath.replace(/\.temp\.js$/, ".abc");
416    if (!fs.existsSync(cacheAbcFilePath)) {
417      console.debug(red, `ERROR ${cacheAbcFilePath} is lost`, reset);
418      process.exitCode = FAIL;
419      break;
420    }
421    let parent = path.join(abcFile, '..');
422    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
423      mkDir(parent);
424    }
425    // for preview mode, cache path and old abc file both exist, should copy abc file for updating
426    if (process.env.cachePath !== undefined) {
427      fs.copyFileSync(cacheAbcFilePath, abcFile);
428    }
429    if (process.env.cachePath === undefined && fs.existsSync(cacheOutputPath)) {
430      fs.unlinkSync(cacheOutputPath);
431    }
432  }
433}
434
435function processExtraAssetForBundle() {
436  writeHashJson();
437  copyFileCachePathToBuildPath();
438  clearGlobalInfo();
439}
440
441function checkNodeModules() {
442  if (process.env.panda === TS2ABC) {
443    let arkEntryPath = path.join(arkDir, 'build');
444    if (isWin) {
445      arkEntryPath = path.join(arkDir, 'build-win');
446    } else if (isMac) {
447      arkEntryPath = path.join(arkDir, 'build-mac');
448    }
449    let nodeModulesPath = path.join(arkEntryPath, NODE_MODULES);
450    validateFilePathLength(nodeModulesPath);
451    if (!(fs.existsSync(nodeModulesPath) && fs.statSync(nodeModulesPath).isDirectory())) {
452      console.error(red, `ERROR: node_modules for ark compiler not found.
453        Please make sure switch to non-root user before runing "npm install" for safity requirements and try re-run "npm install" under ${arkEntryPath}`, reset);
454      return false;
455    }
456  }
457
458  return true;
459}
460
461function initAbcEnv() {
462  let args = [];
463  if (process.env.minPlatformVersion === "8") {
464    process.env.panda = TS2ABC;
465    let js2abc = path.join(arkDir, 'build', 'legacy_api8', 'src', 'index.js');
466    if (isWin) {
467      js2abc = path.join(arkDir, 'build-win', 'legacy_api8', 'src', 'index.js');
468    } else if (isMac) {
469      js2abc = path.join(arkDir, 'build-mac', 'legacy_api8', 'src', 'index.js');
470    }
471    validateFilePathLength(js2abc);
472
473    js2abc = '"' + js2abc + '"';
474    args = [
475      '--expose-gc',
476      js2abc
477    ];
478    if (isDebug) {
479      args.push('--debug');
480    }
481  } else if (process.env.panda === TS2ABC) {
482    let js2abc = path.join(arkDir, 'build', 'src', 'index.js');
483    if (isWin) {
484      js2abc = path.join(arkDir, 'build-win', 'src', 'index.js');
485    } else if (isMac) {
486      js2abc = path.join(arkDir, 'build-mac', 'src', 'index.js');
487    }
488    validateFilePathLength(js2abc);
489
490    js2abc = '"' + js2abc + '"';
491    args = [
492      '--expose-gc',
493      js2abc
494    ];
495    if (isDebug) {
496      args.push('--debug');
497    }
498  } else if (process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined) {
499    let es2abc = path.join(arkDir, 'build', 'bin', 'es2abc');
500    if (isWin) {
501      es2abc = path.join(arkDir, 'build-win', 'bin', 'es2abc.exe');
502    } else if (isMac) {
503      es2abc = path.join(arkDir, 'build-mac', 'bin', 'es2abc');
504    }
505    validateFilePathLength(es2abc);
506
507    args = [
508      '"' + es2abc + '"'
509    ];
510    if (isDebug) {
511      args.push('--debug-info');
512    }
513  }  else {
514    console.debug(red, `ERROR: please set panda module`, reset);
515  }
516
517  return args;
518}
519
520export function isWindows() {
521  return os.type() === WINDOWS;
522}
523
524export function isLinux() {
525  return os.type() === LINUX;
526}
527
528export function isMacOs() {
529  return os.type() === MAC;
530}
531
532export function isOpenHarmony() {
533  return os.platform() === OPENHARMONY;
534}
535
536export function maxFilePathLength() {
537  if (isWindows()) {
538    return 32766;
539  } else if (isLinux() || isOpenHarmony()) {
540    return 4095;
541  } else if (isMacOs()) {
542    return 1016;
543  } else {
544    return -1;
545  }
546}
547
548export function validateFilePathLength(filePath) {
549  if (maxFilePathLength() < 0) {
550    console.error("Unknown OS platform");
551    process.exitCode = FAIL;
552    return false;
553  } else if (filePath.length > 0 && filePath.length <= maxFilePathLength()) {
554    return true;
555  } else if (filePath.length > maxFilePathLength()) {
556    console.error(`The length of ${filePath} exceeds the limitation of current platform, which is ` +
557    `${maxFilePathLength()}. Please try moving the project folder to avoid deeply nested file path and try again`);
558    process.exitCode = FAIL;
559    return false;
560  } else {
561    console.error("Validate file path failed");
562    process.exitCode = FAIL;
563    return false;
564  }
565}
566
567export function isEs2Abc() {
568  return process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined;
569}
570
571export function isTs2Abc() {
572  return process.env.panda === TS2ABC;
573}
574
575function generateAbcByEs2AbcOfBundleMode(inputPaths) {
576  filterIntermediateJsBundleByHashJson(buildPathInfo, inputPaths);
577  if (fileterIntermediateJsBundle.length === 0) {
578    processExtraAssetForBundle();
579    return;
580  }
581  let filesInfoPath = generateFileOfBundle(fileterIntermediateJsBundle);
582  const fileThreads = os.cpus().length < 16 ? os.cpus().length : 16;
583  let genAbcCmd =
584      `${initAbcEnv().join(' ')} "@${filesInfoPath}" --file-threads "${fileThreads}"`;
585
586  console.debug('gen abc cmd is: ', genAbcCmd);
587  try {
588    if (process.env.isPreview === 'true') {
589      childProcess.execSync(genAbcCmd);
590      processExtraAssetForBundle();
591    } else {
592      const child = childProcess.exec(genAbcCmd);
593      child.on('exit', (code) => {
594        if (code === FAIL) {
595          console.debug(red, "ERROR failed to execute es2abc", reset);
596          process.exit(FAIL);
597        }
598        if (process.env.cachePath === undefined) {
599          unlinkSync(filesInfoPath);
600        }
601        processExtraAssetForBundle();
602      });
603
604      child.on('error', (err) => {
605        console.debug(red, err.toString(), reset);
606        process.exit(FAIL);
607      });
608
609      child.stderr.on('data', (data) => {
610        console.debug(red, data.toString(), reset);
611      });
612    }
613  } catch (e) {
614    console.debug(red, `ERROR failed to generate abc with filesInfo ${filesInfoPath}. Error message: ${e} `, reset);
615    process.env.abcCompileSuccess = 'false';
616    if (process.env.isPreview !== 'true') {
617      process.exit(FAIL);
618    }
619  } finally {
620    if (process.env.isPreview === 'true') {
621      if (process.env.cachePath === undefined) {
622        unlinkSync(filesInfoPath);
623      }
624    }
625  }
626}
627
628function generateFileOfBundle(inputPaths) {
629  let filesInfoPath = buildCachePath(FILESINFO_TXT);
630  inputPaths = removeDuplicateInfoOfBundleList(inputPaths);
631
632  let filesInfo = '';
633  inputPaths.forEach(info => {
634    const cacheOutputPath = info.cacheOutputPath;
635    const recordName = 'null_recordName';
636    const moduleType = 'script';
637    const sourceFile = info.sourceFile;
638    const abcFilePath = cacheOutputPath.replace(/\.temp\.js$/, ".abc");
639    filesInfo += `${cacheOutputPath};${recordName};${moduleType};${sourceFile};${abcFilePath}\n`;
640  });
641  fs.writeFileSync(filesInfoPath, filesInfo, 'utf-8');
642
643  return filesInfoPath;
644}
645
646function removeDuplicateInfoOfBundleList(inputPaths) {
647  let tempInputPaths = [];
648  inputPaths.forEach((item) => {
649    const check = tempInputPaths.every((newItem) => {
650      return item.path !== newItem.path;
651    });
652    if (check) {
653      tempInputPaths.push(item);
654    }
655  });
656  inputPaths = tempInputPaths;
657
658  return inputPaths;
659}
660
661function buildCachePath(tailName) {
662  let pathName = process.env.cachePath !== undefined ?
663      path.join(process.env.cachePath, tailName) : path.join(buildPathInfo, tailName);
664  validateFilePathLength(pathName);
665
666  return pathName;
667}
668
669function unlinkSync(filePath) {
670  if (fs.existsSync(filePath)) {
671    fs.unlinkSync(filePath);
672  }
673}
674
675function initCmdPrefix(abcArgs) {
676  let cmdPrefix = "";
677  if (process.env.panda === TS2ABC) {
678    cmdPrefix = `${nodeJs} ${abcArgs.join(' ')}`;
679  } else if (process.env.panda === ES2ABC  || process.env.panda === 'undefined' || process.env.panda === undefined) {
680    cmdPrefix = `${abcArgs.join(' ')}`;
681  } else {
682    console.debug(red, `ERROR please set panda module`, reset);
683  }
684
685  return cmdPrefix;
686}
687
688function processWorkersOfPreviewMode(splittedData, cmdPrefix, workerNumber) {
689  let processEnv = Object.assign({}, process.env);
690  let arkEnvParams = {
691    'splittedData': JSON.stringify(splittedData),
692    'cmdPrefix': cmdPrefix,
693    'workerNumber': workerNumber.toString()
694  };
695  processEnv.arkEnvParams = JSON.stringify(arkEnvParams);
696
697  let genAbcCmd = `${nodeJs} "${path.resolve(__dirname, manageBunldeWorkersScript)}"`;
698  childProcess.execSync(genAbcCmd, {env: processEnv});
699  processExtraAssetForBundle();
700}
701
702function processWorkersOfBuildMode(splittedData, cmdPrefix, workerNumber) {
703  const clusterNewApiVersion = 16;
704  const currentNodeVersion = parseInt(process.version.split(".")[0]);
705  const useNewApi = currentNodeVersion >= clusterNewApiVersion;
706
707  if (useNewApi && cluster.isPrimary || !useNewApi && cluster.isMaster) {
708    if (useNewApi) {
709      cluster.setupPrimary({
710        exec: path.resolve(__dirname, genAbcScript)
711      });
712    } else {
713      cluster.setupMaster({
714        exec: path.resolve(__dirname, genAbcScript)
715      });
716    }
717
718    for (let i = 0; i < workerNumber; ++i) {
719      let workerData = {
720        'inputs': JSON.stringify(splittedData[i]),
721        'cmd': cmdPrefix
722      };
723      cluster.fork(workerData);
724    }
725
726    cluster.on('exit', (worker, code, signal) => {
727      if (code === FAIL) {
728        process.exitCode = FAIL;
729      }
730      console.debug(`worker ${worker.process.pid} finished`);
731    });
732
733    process.on('exit', (code) => {
734      if (process.exitCode !== FAIL && process.env.isPreview !== 'true') {
735        processExtraAssetForBundle();
736      }
737    });
738  }
739}
740
741function clearWebpackCacheByBuildInfo() {
742  if (!process.env.cachePath) {
743    return;
744  }
745
746  // clear&update cache dir when build info is different from last time
747  const cachePrebuildInfoPath = path.join(process.env.cachePath, PREBUILDINFO_JSON);
748  if (fs.existsSync(cachePrebuildInfoPath)) {
749    let cachedJson = undefined;
750    try {
751      cachedJson = JSON.parse(fs.readFileSync(cachePrebuildInfoPath).toString());
752    } catch {
753      removeHashJsonFile();
754      return;
755    }
756    // api version is 8 or 9
757    if (cachedJson && cachedJson.minAPIVersion &&
758      cachedJson.minAPIVersion.toString() !== process.env.minPlatformVersion) {
759      removeHashJsonFile();
760    }
761  }
762}
763
764function setPrebuildInfo() {
765  if (process.env.cachePath && process.env.minPlatformVersion) {
766    let cachedJson = {};
767    const cachePrebuildInfoPath = path.join(process.env.cachePath, PREBUILDINFO_JSON);
768    validateFilePathLength(cachePrebuildInfoPath);
769    cachedJson.minAPIVersion = process.env.minPlatformVersion;
770    fs.writeFile(cachePrebuildInfoPath, JSON.stringify(cachedJson, null, 2), 'utf-8',
771      (err) => {
772        if (err) {
773          logger.error(red, `ArkTS:ERROR Failed to write bundle build info.`, reset);
774        }
775      }
776    );
777  }
778}
779
780function removeHashJsonFile() {
781  const hashFilePath = genHashJsonPath(buildPathInfo);
782  if (hashFilePath.length !== 0 && fs.existsSync(hashFilePath)) {
783    fs.unlinkSync(hashFilePath);
784  }
785}
786