• 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  TS2ABC,
29  FAIL
30} = require('./lib/pre_define');
31
32const {
33  checkAotConfig
34} = require('./lib/gen_aot');
35
36const {
37  configure,
38  getLogger
39} = require('log4js');
40
41configure({
42  appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}},
43  categories: {'default': {appenders: ['ETS'], level: 'info'}}
44});
45const logger = getLogger('ETS');
46
47const staticPreviewPage = process.env.aceStaticPreview;
48const aceCompileMode = process.env.aceCompileMode || 'page';
49const abilityConfig = {
50  abilityType: process.env.abilityType || 'page',
51  abilityEntryFile: null,
52  projectAbilityPath: [],
53  testRunnerFile: []
54};
55const projectConfig = {};
56const resources = {
57  app: {},
58  sys: {}
59};
60const systemModules = [];
61const abilityPagesFullPath = [];
62
63function initProjectConfig(projectConfig) {
64  projectConfig.entryObj = {};
65  projectConfig.cardObj = {};
66  projectConfig.projectPath = projectConfig.projectPath || process.env.aceModuleRoot ||
67    path.join(process.cwd(), 'sample');
68  projectConfig.buildPath = projectConfig.buildPath || process.env.aceModuleBuild ||
69    path.resolve(projectConfig.projectPath, 'build');
70  projectConfig.aceModuleBuild = projectConfig.buildPath;  // To be compatible with both webpack and rollup
71  projectConfig.manifestFilePath = projectConfig.manifestFilePath || process.env.aceManifestPath ||
72    path.join(projectConfig.projectPath, 'manifest.json');
73  projectConfig.aceProfilePath = projectConfig.aceProfilePath || process.env.aceProfilePath;
74  projectConfig.aceModuleJsonPath = projectConfig.aceModuleJsonPath || process.env.aceModuleJsonPath;
75  projectConfig.aceSuperVisualPath = projectConfig.aceSuperVisualPath ||
76    process.env.aceSuperVisualPath;
77  projectConfig.hashProjectPath = projectConfig.hashProjectPath ||
78    hashProjectPath(projectConfig.projectPath);
79  projectConfig.aceBuildJson = projectConfig.aceBuildJson || process.env.aceBuildJson;
80  projectConfig.cachePath = projectConfig.cachePath || process.env.cachePath ||
81    path.resolve(__dirname, 'node_modules/.cache');
82  projectConfig.aceSoPath = projectConfig.aceSoPath || process.env.aceSoPath;
83  projectConfig.xtsMode = /ets_loader_ark$/.test(__dirname);
84  projectConfig.localPropertiesPath = projectConfig.localPropertiesPath || process.env.localPropertiesPath;
85  projectConfig.projectProfilePath = projectConfig.projectProfilePath || process.env.projectProfilePath;
86  projectConfig.isPreview = projectConfig.isPreview || process.env.isPreview === 'true';
87  projectConfig.compileMode = projectConfig.compileMode || 'jsbundle';
88  projectConfig.runtimeOS = projectConfig.runtimeOS || process.env.runtimeOS || 'default';
89  projectConfig.sdkInfo = projectConfig.sdkInfo || process.env.sdkInfo || 'default';
90  projectConfig.splitCommon = false;
91  projectConfig.compileHar = false;
92  projectConfig.compileShared = false;
93  projectConfig.checkEntry = projectConfig.checkEntry || process.env.checkEntry;
94  projectConfig.obfuscateHarType = projectConfig.obfuscateHarType || process.env.obfuscate;
95  projectConfig.packageDir = 'node_modules';
96  projectConfig.packageJson = 'package.json';
97  projectConfig.packageManagerType = 'npm';
98  projectConfig.cardEntryObj = {};
99}
100
101function loadEntryObj(projectConfig) {
102  let manifest = {};
103  initProjectConfig(projectConfig);
104  loadBuildJson();
105  if (process.env.aceManifestPath && aceCompileMode === 'page') {
106    setEntryFile(projectConfig);
107    setFaTestRunnerFile(projectConfig);
108  }
109  if (process.env.aceModuleJsonPath) {
110    setAbilityPages(projectConfig);
111    setStageTestRunnerFile(projectConfig);
112  }
113
114  if (staticPreviewPage) {
115    projectConfig.entryObj['./' + staticPreviewPage] = projectConfig.projectPath + path.sep +
116      staticPreviewPage + '.ets?entry';
117  } else if (abilityConfig.abilityType === 'page') {
118    if (fs.existsSync(projectConfig.manifestFilePath)) {
119      const jsonString = fs.readFileSync(projectConfig.manifestFilePath).toString();
120      manifest = JSON.parse(jsonString);
121      if (manifest && manifest.minPlatformVersion) {
122        process.env.minPlatformVersion = manifest.minPlatformVersion;
123        partialUpdateController(manifest.minPlatformVersion);
124      }
125      projectConfig.pagesJsonFileName = 'config.json';
126    } else if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
127      process.env.compileMode = 'moduleJson';
128      buildManifest(manifest, projectConfig.aceModuleJsonPath);
129    } else {
130      throw Error('\u001b[31m ERROR: the manifest file ' + projectConfig.manifestFilePath.replace(/\\/g, '/') +
131        ' or module.json is lost or format is invalid. \u001b[39m').message;
132    }
133    if (!projectConfig.compileHar) {
134      if (manifest.pages) {
135        const pages = manifest.pages;
136        pages.forEach((element) => {
137          const sourcePath = element.replace(/^\.\/ets\//, '');
138          const fileName = path.resolve(projectConfig.projectPath, sourcePath + '.ets');
139          if (fs.existsSync(fileName)) {
140            projectConfig.entryObj['./' + sourcePath] = fileName + '?entry';
141          } else {
142            throw Error(`\u001b[31m ERROR: page '${fileName.replace(/\\/g, '/')}' does not exist. \u001b[39m`)
143              .message;
144          }
145        });
146      } else {
147        throw Error('\u001b[31m ERROR: missing pages attribute in ' +
148          projectConfig.manifestFilePath.replace(/\\/g, '/') +
149          '. \u001b[39m').message;
150      }
151    }
152  }
153}
154
155function buildManifest(manifest, aceConfigPath) {
156  try {
157    const moduleConfigJson = JSON.parse(fs.readFileSync(aceConfigPath).toString());
158    manifest.type = process.env.abilityType;
159    if (moduleConfigJson && moduleConfigJson.app && moduleConfigJson.app.minAPIVersion) {
160      if (moduleConfigJson.module && moduleConfigJson.module.metadata) {
161        partialUpdateController(moduleConfigJson.app.minAPIVersion, moduleConfigJson.module.metadata);
162        stageOptimization(moduleConfigJson.module.metadata);
163      } else {
164        partialUpdateController(moduleConfigJson.app.minAPIVersion);
165      }
166    }
167    if (moduleConfigJson.module) {
168      switch (moduleConfigJson.module.type) {
169        case 'har':
170          projectConfig.compileHar = true;
171          getPackageJsonEntryPath();
172          break;
173        case 'shared':
174          projectConfig.compileShared = true;
175          getPackageJsonEntryPath();
176          manifest.pages = getPages(moduleConfigJson);
177          break;
178        default:
179          manifest.pages = getPages(moduleConfigJson);
180          break;
181      }
182    } else {
183      throw Error('\u001b[31m' +
184        'ERROR: the config.json file miss key word module || module[abilities].' +
185        '\u001b[39m').message;
186    }
187  } catch (e) {
188    if (/BUIDERROR/.test(e)) {
189      throw Error(e.replace('BUIDERROR', 'ERROR')).message;
190    } else {
191      throw Error('\x1B[31m' + 'ERROR: the module.json file is lost or format is invalid.' +
192        '\x1B[39m').message;
193    }
194  }
195}
196
197function getPackageJsonEntryPath() {
198  const rootPackageJsonPath = path.resolve(projectConfig.projectPath, '../../../' + projectConfig.packageJson);
199  if (fs.existsSync(rootPackageJsonPath)) {
200    const rootPackageJsonContent =
201      (projectConfig.packageManagerType === 'npm' ? JSON : JSON5).parse(fs.readFileSync(rootPackageJsonPath, 'utf-8'));
202    if (rootPackageJsonContent) {
203      if (rootPackageJsonContent.module) {
204        getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath);
205      } else if (rootPackageJsonContent.main) {
206        getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath);
207      } else {
208        getEntryPath('', rootPackageJsonPath);
209      }
210    } else if (projectConfig.compileHar) {
211      throw Error('\u001b[31m' + 'lack message in ' + projectConfig.packageJson + '.' + '\u001b[39m').message;
212    }
213  }
214}
215
216function supportSuffix(mainEntryPath) {
217  if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) {
218    mainEntryPath = path.join(mainEntryPath, 'index.ets');
219  } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) {
220    mainEntryPath = path.join(mainEntryPath, 'index.ts');
221  } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) {
222    mainEntryPath = path.join(mainEntryPath, 'index.js');
223  } else if (projectConfig.compileHar) {
224    throw Error('\u001b[31m' + 'not find entry file in ' + projectConfig.packageJson + '.' + '\u001b[39m').message;
225  }
226  return mainEntryPath;
227}
228
229function supportExtName(mainEntryPath) {
230  if (path.extname(mainEntryPath) === '') {
231    if (fs.existsSync(mainEntryPath + '.ets')) {
232      mainEntryPath = mainEntryPath + '.ets';
233    } else if (fs.existsSync(mainEntryPath + '.ts')) {
234      mainEntryPath = mainEntryPath + '.ts';
235    } else if (fs.existsSync(mainEntryPath + '.js')) {
236      mainEntryPath = mainEntryPath + '.js';
237    }
238  }
239  return mainEntryPath;
240}
241
242function getEntryPath(entryPath, rootPackageJsonPath) {
243  let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath);
244  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) {
245    mainEntryPath = supportSuffix(mainEntryPath);
246  } else {
247    mainEntryPath = supportExtName(mainEntryPath);
248  }
249  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) {
250    const entryKey = path.relative(projectConfig.projectPath, mainEntryPath);
251    projectConfig.entryObj[entryKey] = mainEntryPath;
252    abilityPagesFullPath.push(path.resolve(mainEntryPath).toLowerCase());
253  } else if (projectConfig.compileHar) {
254    throw Error('\u001b[31m' + 'not find entry file in package.json.' + '\u001b[39m').message;
255  }
256}
257
258function stageOptimization(metadata) {
259  if (Array.isArray(metadata) && metadata.length) {
260    metadata.some(item => {
261      if (item.name && item.name === 'USE_COMMON_CHUNK' &&
262        item.value && item.value === 'true') {
263        projectConfig.splitCommon = true;
264        return true;
265      }
266    });
267  }
268}
269
270function getPages(configJson) {
271  const pages = [];
272  let pagesJsonFileName = '';
273  // pages is not necessary in shared library
274  if (projectConfig.compileShared && configJson.module && configJson.module.pages || !projectConfig.compileShared) {
275    pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`;
276  } else {
277    return pages;
278  }
279  const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName);
280  if (fs.existsSync(modulePagePath)) {
281    try {
282      const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
283      if (pagesConfig && pagesConfig.src) {
284        projectConfig.pagesJsonFileName = pagesJsonFileName;
285        return pagesConfig.src;
286      }
287    } catch (e) {
288      throw Error("\x1B[31m" + `BUIDERROR: the ${modulePagePath} file format is invalid.` +
289        "\x1B[39m").message;
290    }
291  }
292  return pages;
293}
294
295function setEntryFile(projectConfig) {
296  const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType;
297  const extendFile = entryFileName === 'app' ? '.ets' : '.ts';
298  const entryFileRealPath = entryFileName + extendFile;
299  const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath);
300  abilityConfig.abilityEntryFile = entryFilePath;
301  if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') {
302    throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message;
303  }
304  projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry';
305}
306
307function setAbilityPages(projectConfig) {
308  let abilityPages = [];
309  if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
310    const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString());
311    abilityPages = readAbilityEntrance(moduleJson);
312    setAbilityFile(projectConfig, abilityPages);
313    setBundleModuleInfo(projectConfig, moduleJson);
314  }
315}
316
317function setFaTestRunnerFile(projectConfig) {
318  const index =projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
319  const testRunnerPath = path.resolve(projectConfig.projectPath.substring(0,index + 1), "TestRunner");
320  if (fs.existsSync(testRunnerPath)) {
321    const testRunnerFiles = [];
322    readFile(testRunnerPath, testRunnerFiles);
323    testRunnerFiles.forEach((item) => {
324      if (/\.(ts|js|ets)$/.test(item)) {
325        const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
326        projectConfig.entryObj["../TestRunner/" + relativePath] = item;
327        abilityConfig.testRunnerFile.push(item);
328      }
329    });
330  }
331}
332
333function setStageTestRunnerFile(projectConfig) {
334  const index =projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
335  const testRunnerPath = path.resolve(projectConfig.projectPath, "TestRunner");
336  if (fs.existsSync(testRunnerPath)) {
337    const testRunnerFiles = [];
338    readFile(testRunnerPath, testRunnerFiles);
339    testRunnerFiles.forEach((item) => {
340      if (/\.(ts|js|ets)$/.test(item)) {
341        const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
342        projectConfig.entryObj["./TestRunner/" + relativePath] = item;
343        abilityConfig.testRunnerFile.push(item);
344      }
345    });
346  }
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