• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2020 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 md5 = require('md5');
19
20const {
21  readFile,
22  writeFileSync
23} = require('./lib/utils');
24
25const {
26  WORKERS_DIR,
27  TS2ABC,
28  FAIL
29} = require('./lib/pre_define');
30
31const {
32  checkAotConfig
33} = require('./lib/gen_aot');
34
35const {
36  configure,
37  getLogger
38} = require('log4js');
39
40configure({
41  appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}},
42  categories: {'default': {appenders: ['ETS'], level: 'info'}}
43});
44const logger = getLogger('ETS');
45
46const staticPreviewPage = process.env.aceStaticPreview;
47const aceCompileMode = process.env.aceCompileMode || 'page';
48const abilityConfig = {
49  abilityType: process.env.abilityType || 'page',
50  abilityEntryFile: null,
51  projectAbilityPath: [],
52  testRunnerFile: []
53};
54const projectConfig = {};
55const resources = {
56  app: {},
57  sys: {}
58};
59const systemModules = [];
60const abilityPagesFullPath = [];
61
62function initProjectConfig(projectConfig) {
63  projectConfig.entryObj = {};
64  projectConfig.cardObj = {};
65  projectConfig.projectPath = projectConfig.projectPath || process.env.aceModuleRoot ||
66    path.join(process.cwd(), 'sample');
67  projectConfig.buildPath = projectConfig.buildPath || process.env.aceModuleBuild ||
68    path.resolve(projectConfig.projectPath, 'build');
69  projectConfig.aceModuleBuild = projectConfig.buildPath;  // To be compatible with both webpack and rollup
70  projectConfig.manifestFilePath = projectConfig.manifestFilePath || process.env.aceManifestPath ||
71    path.join(projectConfig.projectPath, 'manifest.json');
72  projectConfig.aceProfilePath = projectConfig.aceProfilePath || process.env.aceProfilePath;
73  projectConfig.aceModuleJsonPath = projectConfig.aceModuleJsonPath || process.env.aceModuleJsonPath;
74  projectConfig.aceSuperVisualPath = projectConfig.aceSuperVisualPath ||
75    process.env.aceSuperVisualPath;
76  projectConfig.hashProjectPath = projectConfig.hashProjectPath ||
77    hashProjectPath(projectConfig.projectPath);
78  projectConfig.aceBuildJson = projectConfig.aceBuildJson || process.env.aceBuildJson;
79  projectConfig.cachePath = projectConfig.cachePath || process.env.cachePath ||
80    path.resolve(__dirname, 'node_modules/.cache');
81  projectConfig.aceSoPath = projectConfig.aceSoPath || process.env.aceSoPath;
82  projectConfig.xtsMode = /ets_loader_ark$/.test(__dirname);
83  projectConfig.localPropertiesPath = projectConfig.localPropertiesPath || process.env.localPropertiesPath;
84  projectConfig.projectProfilePath = projectConfig.projectProfilePath || process.env.projectProfilePath;
85  projectConfig.isPreview = projectConfig.isPreview || process.env.isPreview === 'true';
86  projectConfig.compileMode = projectConfig.compileMode || 'jsbundle';
87  projectConfig.runtimeOS = projectConfig.runtimeOS || process.env.runtimeOS || 'default';
88  projectConfig.sdkInfo = projectConfig.sdkInfo || process.env.sdkInfo || 'default';
89  projectConfig.splitCommon = false;
90  projectConfig.compileHar = false;
91  projectConfig.compileShared = false;
92  projectConfig.checkEntry = projectConfig.checkEntry || process.env.checkEntry;
93  projectConfig.obfuscateHarType = projectConfig.obfuscateHarType || process.env.obfuscate;
94  projectConfig.packageDir = 'node_modules';
95  projectConfig.packageJson = 'package.json';
96  projectConfig.cardEntryObj = {};
97}
98
99function loadEntryObj(projectConfig) {
100  let manifest = {};
101  initProjectConfig(projectConfig);
102  loadBuildJson();
103  if (process.env.aceManifestPath && aceCompileMode === 'page') {
104    setEntryFile(projectConfig);
105    setFaTestRunnerFile(projectConfig);
106  }
107  if (process.env.aceModuleJsonPath) {
108    setAbilityPages(projectConfig);
109    setStageTestRunnerFile(projectConfig);
110  }
111
112  if (staticPreviewPage) {
113    projectConfig.entryObj['./' + staticPreviewPage] = projectConfig.projectPath + path.sep +
114      staticPreviewPage + '.ets?entry';
115  } else if (abilityConfig.abilityType === 'page') {
116    if (fs.existsSync(projectConfig.manifestFilePath)) {
117      const jsonString = fs.readFileSync(projectConfig.manifestFilePath).toString();
118      manifest = JSON.parse(jsonString);
119      if (manifest && manifest.minPlatformVersion) {
120        process.env.minPlatformVersion = manifest.minPlatformVersion;
121        partialUpdateController(manifest.minPlatformVersion);
122      }
123      projectConfig.pagesJsonFileName = 'config.json';
124    } else if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
125      process.env.compileMode = 'moduleJson';
126      buildManifest(manifest, projectConfig.aceModuleJsonPath);
127    } else {
128      throw Error('\u001b[31m ERROR: the manifest file ' + projectConfig.manifestFilePath.replace(/\\/g, '/') +
129        ' or module.json is lost or format is invalid. \u001b[39m').message;
130    }
131    if (!projectConfig.compileHar) {
132      if (manifest.pages) {
133        const pages = manifest.pages;
134        pages.forEach((element) => {
135          const sourcePath = element.replace(/^\.\/ets\//, '');
136          const fileName = path.resolve(projectConfig.projectPath, sourcePath + '.ets');
137          if (fs.existsSync(fileName)) {
138            projectConfig.entryObj['./' + sourcePath] = fileName + '?entry';
139          } else {
140            throw Error(`\u001b[31m ERROR: page '${fileName.replace(/\\/g, '/')}' does not exist. \u001b[39m`)
141              .message;
142          }
143        });
144      } else {
145        throw Error('\u001b[31m ERROR: missing pages attribute in ' +
146          projectConfig.manifestFilePath.replace(/\\/g, '/') +
147          '. \u001b[39m').message;
148      }
149    }
150  }
151}
152
153function buildManifest(manifest, aceConfigPath) {
154  try {
155    const moduleConfigJson = JSON.parse(fs.readFileSync(aceConfigPath).toString());
156    manifest.type = process.env.abilityType;
157    if (moduleConfigJson && moduleConfigJson.app && moduleConfigJson.app.minAPIVersion) {
158      if (moduleConfigJson.module && moduleConfigJson.module.metadata) {
159        partialUpdateController(moduleConfigJson.app.minAPIVersion, moduleConfigJson.module.metadata);
160        stageOptimization(moduleConfigJson.module.metadata);
161      } else {
162        partialUpdateController(moduleConfigJson.app.minAPIVersion);
163      }
164    }
165    if (moduleConfigJson.module) {
166      switch (moduleConfigJson.module.type) {
167        case 'har':
168          projectConfig.compileHar = true;
169          getPackageJsonEntryPath();
170          break;
171        case 'shared':
172          projectConfig.compileShared = true;
173          getPackageJsonEntryPath();
174          manifest.pages = getPages(moduleConfigJson);
175          break;
176        default:
177          manifest.pages = getPages(moduleConfigJson);
178          break;
179      }
180    } else {
181      throw Error('\u001b[31m' +
182        'ERROR: the config.json file miss key word module || module[abilities].' +
183        '\u001b[39m').message;
184    }
185  } catch (e) {
186    if (/BUIDERROR/.test(e)) {
187      throw Error(e.replace('BUIDERROR', 'ERROR')).message;
188    } else {
189      throw Error('\x1B[31m' + 'ERROR: the module.json file is lost or format is invalid.' +
190        '\x1B[39m').message;
191    }
192  }
193}
194
195function getPackageJsonEntryPath() {
196  const rootPackageJsonPath = path.resolve(projectConfig.projectPath, '../../../' + projectConfig.packageJson);
197  if (fs.existsSync(rootPackageJsonPath)) {
198    const rootPackageJsonContent = JSON.parse(fs.readFileSync(rootPackageJsonPath, 'utf-8'));
199    if (rootPackageJsonContent) {
200      if (rootPackageJsonContent.module) {
201        getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath);
202      } else if (rootPackageJsonContent.main) {
203        getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath);
204      } else {
205        getEntryPath('', rootPackageJsonPath);
206      }
207    } else if (projectConfig.compileHar) {
208      throw Error('\u001b[31m' + 'lack message in ' + projectConfig.packageJson + '.' + '\u001b[39m').message;
209    }
210  }
211}
212
213function supportSuffix(mainEntryPath) {
214  if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) {
215    mainEntryPath = path.join(mainEntryPath, 'index.ets');
216  } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) {
217    mainEntryPath = path.join(mainEntryPath, 'index.ts');
218  } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) {
219    mainEntryPath = path.join(mainEntryPath, 'index.js');
220  } else if (projectConfig.compileHar) {
221    throw Error('\u001b[31m' + 'not find entry file in ' + projectConfig.packageJson + '.' + '\u001b[39m').message;
222  }
223  return mainEntryPath;
224}
225
226function supportExtName(mainEntryPath) {
227  if (path.extname(mainEntryPath) === '') {
228    if (fs.existsSync(mainEntryPath + '.ets')) {
229      mainEntryPath = mainEntryPath + '.ets';
230    } else if (fs.existsSync(mainEntryPath + '.ts')) {
231      mainEntryPath = mainEntryPath + '.ts';
232    } else if (fs.existsSync(mainEntryPath + '.js')) {
233      mainEntryPath = mainEntryPath + '.js';
234    }
235  }
236  return mainEntryPath;
237}
238
239function getEntryPath(entryPath, rootPackageJsonPath) {
240  let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath);
241  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) {
242    mainEntryPath = supportSuffix(mainEntryPath);
243  } else {
244    mainEntryPath = supportExtName(mainEntryPath);
245  }
246  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) {
247    const entryKey = path.relative(projectConfig.projectPath, mainEntryPath);
248    projectConfig.entryObj[entryKey] = mainEntryPath;
249    abilityPagesFullPath.push(mainEntryPath);
250  } else if (projectConfig.compileHar) {
251    throw Error('\u001b[31m' + 'not find entry file in package.json.' + '\u001b[39m').message;
252  }
253}
254
255function stageOptimization(metadata) {
256  if (Array.isArray(metadata) && metadata.length) {
257    metadata.some(item => {
258      if (item.name && item.name === 'USE_COMMON_CHUNK' &&
259        item.value && item.value === 'true') {
260        projectConfig.splitCommon = true;
261        return true;
262      }
263    });
264  }
265}
266
267function getPages(configJson) {
268  const pages = [];
269  const pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`;
270  const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName);
271  if (fs.existsSync(modulePagePath)) {
272    try {
273      const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
274      if (pagesConfig && pagesConfig.src) {
275        projectConfig.pagesJsonFileName = pagesJsonFileName;
276        return pagesConfig.src;
277      }
278    } catch (e) {
279      throw Error("\x1B[31m" + `BUIDERROR: the ${modulePagePath} file format is invalid.` +
280        "\x1B[39m").message;
281    }
282  }
283  return pages;
284}
285
286function setEntryFile(projectConfig) {
287  const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType;
288  const extendFile = entryFileName === 'app' ? '.ets' : '.ts';
289  const entryFileRealPath = entryFileName + extendFile;
290  const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath);
291  abilityConfig.abilityEntryFile = entryFilePath;
292  if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') {
293    throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message;
294  }
295  projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry';
296}
297
298function setAbilityPages(projectConfig) {
299  let abilityPages = [];
300  if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
301    const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString());
302    abilityPages = readAbilityEntrance(moduleJson);
303    setAbilityFile(projectConfig, abilityPages);
304    setBundleModuleInfo(projectConfig, moduleJson);
305  }
306}
307
308function setFaTestRunnerFile(projectConfig) {
309  const index =projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
310  const testRunnerPath = path.resolve(projectConfig.projectPath.substring(0,index + 1), "TestRunner");
311  if (fs.existsSync(testRunnerPath)) {
312    const testRunnerFiles = [];
313    readFile(testRunnerPath, testRunnerFiles);
314    testRunnerFiles.forEach((item) => {
315      if (/\.(ts|js|ets)$/.test(item)) {
316        const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
317		projectConfig.entryObj["../TestRunner/" + relativePath] = item;
318        abilityConfig.testRunnerFile.push(item);
319      }
320    })
321  }
322}
323
324function setStageTestRunnerFile(projectConfig) {
325  const index =projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
326  const testRunnerPath = path.resolve(projectConfig.projectPath, "TestRunner");
327  if (fs.existsSync(testRunnerPath)) {
328    const testRunnerFiles = [];
329    readFile(testRunnerPath, testRunnerFiles);
330    testRunnerFiles.forEach((item) => {
331      if (/\.(ts|js|ets)$/.test(item)) {
332        const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
333		projectConfig.entryObj["./TestRunner/" + relativePath] = item;
334        abilityConfig.testRunnerFile.push(item);
335      }
336    })
337  }
338}
339
340function setBundleModuleInfo(projectConfig, moduleJson) {
341  if (moduleJson.module) {
342    projectConfig.moduleName = moduleJson.module.name;
343  }
344  if (moduleJson.app) {
345    projectConfig.bundleName = moduleJson.app.bundleName;
346  }
347}
348
349function setAbilityFile(projectConfig, abilityPages) {
350  abilityPages.forEach(abilityPath => {
351    const projectAbilityPath = path.resolve(projectConfig.projectPath, '../', abilityPath);
352    if (path.isAbsolute(abilityPath)) {
353      abilityPath = '.' + abilityPath.slice(projectConfig.projectPath.length);
354    }
355    const entryPageKey = abilityPath.replace(/^\.\/ets\//, './').replace(/\.ts$/, '').replace(/\.ets$/, '');
356    if (fs.existsSync(projectAbilityPath)) {
357      abilityConfig.projectAbilityPath.push(projectAbilityPath);
358      projectConfig.entryObj[entryPageKey] = projectAbilityPath + '?entry';
359    } else {
360      throw Error(
361        `\u001b[31m ERROR: srcEntry file '${projectAbilityPath.replace(/\\/g, '/')}' does not exist. \u001b[39m`
362      ).message;
363    }
364  });
365}
366
367function readAbilityEntrance(moduleJson) {
368  let abilityPages = [];
369  if (moduleJson.module) {
370    const moduleSrcEntrance = moduleJson.module.srcEntrance;
371    const moduleSrcEntry = moduleJson.module.srcEntry;
372    if (moduleSrcEntry) {
373      abilityPages.push(moduleSrcEntry);
374      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry));
375    } else if (moduleSrcEntrance) {
376      abilityPages.push(moduleSrcEntrance);
377      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance));
378    }
379    if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) {
380      setEntrance(moduleJson.module.abilities, abilityPages);
381    }
382    if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length > 0) {
383      setEntrance(moduleJson.module.extensionAbilities, abilityPages);
384      setCardPages(moduleJson.module.extensionAbilities);
385    }
386  }
387  return abilityPages;
388}
389
390function setEntrance(abilityConfig, abilityPages) {
391  if (abilityConfig && abilityConfig.length > 0) {
392    abilityConfig.forEach(ability => {
393      if (ability.srcEntry) {
394        abilityPages.push(ability.srcEntry)
395        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry))
396      } else if (ability.srcEntrance) {
397        abilityPages.push(ability.srcEntrance);
398        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance));
399      }
400    });
401  }
402}
403
404function setCardPages(extensionAbilities) {
405  if (extensionAbilities && extensionAbilities.length > 0) {
406    extensionAbilities.forEach(extensionAbility => {
407      if (extensionAbility.metadata) {
408        extensionAbility.metadata.forEach(metadata => {
409          if (metadata.resource) {
410            readCardResource(metadata.resource);
411          }
412        })
413      }
414    });
415  }
416}
417
418function readCardResource(resource) {
419  const cardJsonFileName = `${resource.replace(/\$profile\:/, '')}.json`;
420  const modulePagePath = path.resolve(projectConfig.aceProfilePath, cardJsonFileName);
421  if (fs.existsSync(modulePagePath)) {
422    const cardConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
423    if (cardConfig.forms) {
424      cardConfig.forms.forEach(form => {
425        readCardForm(form);
426      })
427    }
428  }
429}
430
431function readCardForm(form) {
432  if ((form.type && form.type === 'eTS') ||
433    (form.uiSyntax && form.uiSyntax === 'arkts')) {
434    const sourcePath = form.src.replace(/\.ets$/, '');
435    const cardPath = path.resolve(projectConfig.projectPath, '..', sourcePath + '.ets');
436    if (cardPath && fs.existsSync(cardPath)) {
437      projectConfig.entryObj['../' + sourcePath] = cardPath + '?entry';
438      projectConfig.cardEntryObj['../' + sourcePath] = cardPath;
439      projectConfig.cardObj[cardPath] = sourcePath.replace(/^\.\//, '');
440    }
441  }
442}
443
444function getAbilityFullPath(projectPath, abilityPath) {
445  let finalPath = path.resolve(path.resolve(projectPath, '../'), abilityPath);
446  finalPath = finalPath.replace(/\\/g, '/');
447  if (fs.existsSync(finalPath)) {
448    return finalPath;
449  } else {
450    return abilityPath;
451  }
452}
453
454function loadWorker(projectConfig, workerFileEntry) {
455  if (workerFileEntry) {
456    projectConfig.entryObj = Object.assign(projectConfig.entryObj, workerFileEntry);
457  } else {
458    const workerPath = path.resolve(projectConfig.projectPath, WORKERS_DIR);
459    if (fs.existsSync(workerPath)) {
460      const workerFiles = [];
461      readFile(workerPath, workerFiles);
462      workerFiles.forEach((item) => {
463        if (/\.(ts|js)$/.test(item)) {
464          const relativePath = path.relative(workerPath, item)
465            .replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
466          projectConfig.entryObj[`./${WORKERS_DIR}/` + relativePath] = item;
467        }
468      })
469    }
470  }
471}
472
473let aceBuildJson = {};
474function loadBuildJson() {
475  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
476    aceBuildJson = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
477  }
478  if (aceBuildJson.packageManagerType === 'ohpm') {
479    projectConfig.packageDir = 'oh_modules';
480    projectConfig.packageJson = 'oh-package.json5';
481  }
482}
483
484function initBuildInfo() {
485  projectConfig.projectRootPath = aceBuildJson.projectRootPath;
486  if (projectConfig.compileHar && aceBuildJson.moduleName &&
487    aceBuildJson.modulePathMap[aceBuildJson.moduleName]) {
488    projectConfig.moduleRootPath = aceBuildJson.modulePathMap[aceBuildJson.moduleName];
489  }
490}
491
492function readWorkerFile() {
493  const workerFileEntry = {};
494  if (aceBuildJson.workers) {
495    aceBuildJson.workers.forEach(worker => {
496      if (!/\.(ts|js)$/.test(worker)) {
497        worker += '.ts';
498      }
499      const relativePath = path.relative(projectConfig.projectPath, worker);
500      if (filterWorker(relativePath)) {
501        const workerKey = relativePath.replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
502        if (workerFileEntry[workerKey]) {
503          throw Error(
504            '\u001b[31m ERROR: The worker file cannot use the same file name: \n' +
505            workerFileEntry[workerKey] + '\n' + worker + '\u001b[39m'
506          ).message;
507        } else {
508          workerFileEntry[workerKey] = worker;
509        }
510      }
511    });
512    return workerFileEntry;
513  }
514  return null;
515}
516
517function readPatchConfig() {
518  if (aceBuildJson.patchConfig) {
519    projectConfig.hotReload = process.env.watchMode === 'true' && !projectConfig.isPreview;
520    projectConfig.patchAbcPath = aceBuildJson.patchConfig.patchAbcPath;
521    projectConfig.changedFileList = aceBuildJson.patchConfig.changedFileList ?
522      aceBuildJson.patchConfig.changedFileList : path.join(projectConfig.cachePath, 'changedFileList.json');
523    if (projectConfig.hotReload) {
524      writeFileSync(projectConfig.changedFileList, JSON.stringify({
525        modifiedFiles: [],
526        removedFiles: []
527      }));
528    }
529  }
530}
531
532function filterWorker(workerPath) {
533  return /\.(ts|js)$/.test(workerPath);
534}
535
536;(function initSystemResource() {
537  const sysResourcePath = path.resolve(__dirname, './sysResource.js');
538  if (fs.existsSync(sysResourcePath)) {
539    resources.sys = require(sysResourcePath).sys;
540  }
541})();
542
543;(function readSystemModules() {
544  const systemModulesPath = path.resolve(__dirname, '../../api');
545  if (fs.existsSync(systemModulesPath)) {
546    systemModules.push(...fs.readdirSync(systemModulesPath));
547  }
548})()
549
550function readAppResource(filePath) {
551  if (fs.existsSync(filePath)) {
552    const appResource = fs.readFileSync(filePath, "utf-8");
553    const resourceArr = appResource.split(/\n/);
554    let resourceMap = new Map();
555    processResourceArr(resourceArr, resourceMap, filePath);
556    for (let [key, value] of resourceMap) {
557      resources.app[key] = value;
558    }
559  }
560}
561
562function processResourceArr(resourceArr, resourceMap, filePath) {
563  for (let i = 0; i < resourceArr.length; i++) {
564    if (!resourceArr[i].length) {
565      continue;
566    }
567    const resourceData = resourceArr[i].split(/\s/);
568    if (resourceData.length === 3 && !isNaN(Number(resourceData[2])) ) {
569      if (resourceMap.get(resourceData[0])) {
570        const resourceKeys = resourceMap.get(resourceData[0]);
571        if (!resourceKeys[resourceData[1]] || resourceKeys[resourceData[1]] !== Number(resourceData[2])) {
572          resourceKeys[resourceData[1]] = Number(resourceData[2]);
573        }
574      } else {
575        let obj = {};
576        obj[resourceData[1]] = Number(resourceData[2]);
577        resourceMap.set(resourceData[0], obj);
578      }
579    } else {
580      logger.warn(`\u001b[31m ArkTS:WARN The format of file '${filePath}' is incorrect. \u001b[39m`);
581      break;
582    }
583  }
584}
585
586function hashProjectPath(projectPath) {
587  process.env.hashProjectPath = "_" + md5(projectPath);
588  return process.env.hashProjectPath;
589}
590
591function loadModuleInfo(projectConfig, envArgs) {
592  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
593    const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
594    if (buildJsonInfo.compileMode) {
595      projectConfig.compileMode = buildJsonInfo.compileMode;
596    }
597    projectConfig.projectRootPath = buildJsonInfo.projectRootPath;
598    projectConfig.modulePathMap = buildJsonInfo.modulePathMap;
599    projectConfig.isOhosTest = buildJsonInfo.isOhosTest;
600    let faultHandler = function (error) {
601      // rollup's error will be handled in fast build
602      if (process.env.compileTool === 'rollup') {
603        return;
604      }
605      logger.error(error);
606      process.exit(FAIL);
607    }
608    const compileMode = process.env.compileTool === 'rollup' ? projectConfig.compileMode : buildJsonInfo.compileMode;
609    if (checkAotConfig(compileMode, buildJsonInfo, faultHandler)) {
610      projectConfig.processTs = true;
611      projectConfig.pandaMode = TS2ABC;
612      projectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut;
613      projectConfig.anBuildMode = buildJsonInfo.anBuildMode;
614      projectConfig.apPath = buildJsonInfo.apPath;
615    } else {
616      projectConfig.processTs = false;
617      projectConfig.pandaMode = buildJsonInfo.pandaMode;
618    }
619    if (envArgs !== undefined) {
620      projectConfig.buildArkMode = envArgs.buildMode;
621    }
622    if (compileMode === 'esmodule') {
623      projectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath;
624      projectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap;
625    }
626    if (projectConfig.compileHar && buildJsonInfo.moduleName &&
627      buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]) {
628      projectConfig.moduleRootPath = buildJsonInfo.modulePathMap[buildJsonInfo.moduleName];
629    }
630  }
631}
632
633function checkAppResourcePath(appResourcePath, config) {
634  if (appResourcePath) {
635    readAppResource(appResourcePath);
636    if (fs.existsSync(appResourcePath) && config.cache) {
637      config.cache.buildDependencies.config.push(appResourcePath);
638    }
639    if (!projectConfig.xtsMode) {
640      const appResourcePathSavePath = path.resolve(projectConfig.cachePath, 'resource_path.txt');
641      saveAppResourcePath(appResourcePath, appResourcePathSavePath);
642      if (fs.existsSync(appResourcePathSavePath) && config.cache) {
643        config.cache.buildDependencies.config.push(appResourcePathSavePath);
644      }
645    }
646  }
647}
648
649function saveAppResourcePath(appResourcePath, appResourcePathSavePath) {
650  let isSave = false;
651  if (fs.existsSync(appResourcePathSavePath)) {
652    const saveContent = fs.readFileSync(appResourcePathSavePath);
653    if (appResourcePath !== saveContent) {
654      isSave = true;
655    }
656  } else {
657    isSave = true;
658  }
659  if (isSave) {
660    fs.writeFileSync(appResourcePathSavePath, appResourcePath);
661  }
662}
663
664function addSDKBuildDependencies(config) {
665  if (projectConfig.localPropertiesPath &&
666    fs.existsSync(projectConfig.localPropertiesPath) && config.cache) {
667    config.cache.buildDependencies.config.push(projectConfig.localPropertiesPath)
668  }
669  if (projectConfig.projectProfilePath &&
670    fs.existsSync(projectConfig.projectProfilePath) && config.cache) {
671    config.cache.buildDependencies.config.push(projectConfig.projectProfilePath)
672  }
673}
674
675function getCleanConfig(workerFile) {
676  const cleanPath = [];
677  if (projectConfig.compileMode === 'esmodule') {
678    return cleanPath;
679  }
680  cleanPath.push(projectConfig.buildPath);
681  if (workerFile) {
682    const workerFilesPath = Object.keys(workerFile);
683    for (const workerFilePath of workerFilesPath) {
684      cleanPath.push(path.join(projectConfig.buildPath, workerFilePath, '..'));
685    }
686  }
687  return cleanPath;
688}
689
690function isPartialUpdate(metadata) {
691  if (Array.isArray(metadata) && metadata.length) {
692    metadata.some(item => {
693      if (item.name && item.name === 'ArkTSPartialUpdate' &&
694        item.value && item.value === 'false') {
695        partialUpdateConfig.partialUpdateMode = false;
696      }
697      if (item.name && item.name === 'ArkTSBuilderCheck' &&
698        item.value && item.value === 'false') {
699        partialUpdateConfig.builderCheck = false;
700      }
701      return !partialUpdateConfig.partialUpdateMode && !partialUpdateConfig.builderCheck;
702    });
703  }
704}
705
706function partialUpdateController(minAPIVersion, metadata = null) {
707  if (minAPIVersion >= 9) {
708    partialUpdateConfig.partialUpdateMode = true;
709  }
710  if (metadata) {
711    isPartialUpdate(metadata);
712  }
713}
714
715const globalProgram = {
716  program: null,
717  watchProgram: null
718};
719
720const partialUpdateConfig = {
721  partialUpdateMode: false,
722  builderCheck: true
723};
724
725exports.globalProgram = globalProgram;
726exports.projectConfig = projectConfig;
727exports.loadEntryObj = loadEntryObj;
728exports.readAppResource = readAppResource;
729exports.resources = resources;
730exports.loadWorker = loadWorker;
731exports.abilityConfig = abilityConfig;
732exports.readWorkerFile = readWorkerFile;
733exports.abilityPagesFullPath = abilityPagesFullPath;
734exports.loadModuleInfo = loadModuleInfo;
735exports.systemModules = systemModules;
736exports.checkAppResourcePath = checkAppResourcePath;
737exports.addSDKBuildDependencies = addSDKBuildDependencies;
738exports.partialUpdateConfig = partialUpdateConfig;
739exports.readPatchConfig = readPatchConfig;
740exports.initBuildInfo = initBuildInfo;
741exports.getCleanConfig = getCleanConfig;
742