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