• 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        'BUIDERROR: 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    let rootPackageJsonContent;
202    try {
203      rootPackageJsonContent = (projectConfig.packageManagerType === 'npm' ?
204        JSON : JSON5).parse(fs.readFileSync(rootPackageJsonPath, 'utf-8'));
205    } catch (e) {
206      throw Error('\u001b[31m' + 'BUIDERROR: ' + rootPackageJsonPath + ' format is invalid.' + '\u001b[39m').message;
207    }
208    if (rootPackageJsonContent) {
209      if (rootPackageJsonContent.module) {
210        getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath);
211      } else if (rootPackageJsonContent.main) {
212        getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath);
213      } else {
214        getEntryPath('', rootPackageJsonPath);
215      }
216    } else if (projectConfig.compileHar) {
217      throw Error('\u001b[31m' + 'BUIDERROR: lack message in ' + projectConfig.packageJson + '.' +
218        '\u001b[39m').message;
219    }
220  }
221}
222
223function supportSuffix(mainEntryPath) {
224  if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) {
225    mainEntryPath = path.join(mainEntryPath, 'index.ets');
226  } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) {
227    mainEntryPath = path.join(mainEntryPath, 'index.ts');
228  } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) {
229    mainEntryPath = path.join(mainEntryPath, 'index.js');
230  } else if (projectConfig.compileHar) {
231    throw Error('\u001b[31m' + 'BUIDERROR: not find entry file in ' + projectConfig.packageJson +
232      '.' + '\u001b[39m').message;
233  }
234  return mainEntryPath;
235}
236
237function supportExtName(mainEntryPath) {
238  if (path.extname(mainEntryPath) === '') {
239    if (fs.existsSync(mainEntryPath + '.ets')) {
240      mainEntryPath = mainEntryPath + '.ets';
241    } else if (fs.existsSync(mainEntryPath + '.ts')) {
242      mainEntryPath = mainEntryPath + '.ts';
243    } else if (fs.existsSync(mainEntryPath + '.js')) {
244      mainEntryPath = mainEntryPath + '.js';
245    }
246  }
247  return mainEntryPath;
248}
249
250function getEntryPath(entryPath, rootPackageJsonPath) {
251  let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath);
252  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) {
253    mainEntryPath = supportSuffix(mainEntryPath);
254  } else {
255    mainEntryPath = supportExtName(mainEntryPath);
256  }
257  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) {
258    const entryKey = path.relative(projectConfig.projectPath, mainEntryPath);
259    projectConfig.entryObj[entryKey] = mainEntryPath;
260    abilityPagesFullPath.push(path.resolve(mainEntryPath).toLowerCase());
261  } else if (projectConfig.compileHar) {
262    throw Error('\u001b[31m' + `BUIDERROR: not find entry file in ${rootPackageJsonPath}.` + '\u001b[39m').message;
263  }
264}
265
266function stageOptimization(metadata) {
267  if (Array.isArray(metadata) && metadata.length) {
268    metadata.some(item => {
269      if (item.name && item.name === 'USE_COMMON_CHUNK' &&
270        item.value && item.value === 'true') {
271        projectConfig.splitCommon = true;
272        return true;
273      }
274    });
275  }
276}
277
278function getPages(configJson) {
279  const pages = [];
280  let pagesJsonFileName = '';
281  // pages is not necessary in shared library
282  if (projectConfig.compileShared && configJson.module && configJson.module.pages || !projectConfig.compileShared) {
283    pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`;
284  } else {
285    return pages;
286  }
287  const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName);
288  if (fs.existsSync(modulePagePath)) {
289    try {
290      const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
291      if (pagesConfig && pagesConfig.src) {
292        projectConfig.pagesJsonFileName = pagesJsonFileName;
293        return pagesConfig.src;
294      }
295    } catch (e) {
296      throw Error("\x1B[31m" + `BUIDERROR: the ${modulePagePath} file format is invalid.` +
297        "\x1B[39m").message;
298    }
299  }
300  return pages;
301}
302
303function setEntryFile(projectConfig) {
304  const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType;
305  const extendFile = entryFileName === 'app' ? '.ets' : '.ts';
306  const entryFileRealPath = entryFileName + extendFile;
307  const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath);
308  abilityConfig.abilityEntryFile = entryFilePath;
309  if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') {
310    throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message;
311  }
312  projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry';
313}
314
315function setAbilityPages(projectConfig) {
316  let abilityPages = [];
317  if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
318    const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString());
319    abilityPages = readAbilityEntrance(moduleJson);
320    setAbilityFile(projectConfig, abilityPages);
321    setBundleModuleInfo(projectConfig, moduleJson);
322  }
323}
324
325function setTestRunnerFile(projectConfig, isStageBased) {
326  const index =projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
327  TEST_RUNNER_DIR_SET.forEach((dir) => {
328    let projectPath = isStageBased ? projectConfig.projectPath : projectConfig.projectPath.substring(0,index + 1);
329    const testRunnerPath = path.resolve(projectPath, dir);
330    if (fs.existsSync(testRunnerPath)) {
331      const testRunnerFiles = [];
332      readFile(testRunnerPath, testRunnerFiles);
333      testRunnerFiles.forEach((item) => {
334        if (/\.(ts|js|ets)$/.test(item)) {
335          const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
336          if (isStageBased) {
337            projectConfig.entryObj[`./${dir}/${relativePath}`] = item;
338          } else {
339            projectConfig.entryObj[`../${dir}/${relativePath}`] = item;
340          }
341          abilityConfig.testRunnerFile.push(item);
342        }
343      })
344    }
345  });
346}
347
348function setFaTestRunnerFile(projectConfig) {
349  setTestRunnerFile(projectConfig, false);
350}
351
352function setStageTestRunnerFile(projectConfig) {
353  setTestRunnerFile(projectConfig, true);
354}
355
356function setBundleModuleInfo(projectConfig, moduleJson) {
357  if (moduleJson.module) {
358    projectConfig.moduleName = moduleJson.module.name;
359  }
360  if (moduleJson.app) {
361    projectConfig.bundleName = moduleJson.app.bundleName;
362  }
363}
364
365function setAbilityFile(projectConfig, abilityPages) {
366  abilityPages.forEach(abilityPath => {
367    const projectAbilityPath = path.resolve(projectConfig.projectPath, '../', abilityPath);
368    if (path.isAbsolute(abilityPath)) {
369      abilityPath = '.' + abilityPath.slice(projectConfig.projectPath.length);
370    }
371    const entryPageKey = abilityPath.replace(/^\.\/ets\//, './').replace(/\.ts$/, '').replace(/\.ets$/, '');
372    if (fs.existsSync(projectAbilityPath)) {
373      abilityConfig.projectAbilityPath.push(projectAbilityPath);
374      projectConfig.entryObj[entryPageKey] = projectAbilityPath + '?entry';
375    } else {
376      throw Error(
377        `\u001b[31m ERROR: srcEntry file '${projectAbilityPath.replace(/\\/g, '/')}' does not exist. \u001b[39m`
378      ).message;
379    }
380  });
381}
382
383function readAbilityEntrance(moduleJson) {
384  const abilityPages = [];
385  if (moduleJson.module) {
386    const moduleSrcEntrance = moduleJson.module.srcEntrance;
387    const moduleSrcEntry = moduleJson.module.srcEntry;
388    if (moduleSrcEntry) {
389      abilityPages.push(moduleSrcEntry);
390      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry));
391    } else if (moduleSrcEntrance) {
392      abilityPages.push(moduleSrcEntrance);
393      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance));
394    }
395    if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) {
396      setEntrance(moduleJson.module.abilities, abilityPages);
397    }
398    if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length > 0) {
399      setEntrance(moduleJson.module.extensionAbilities, abilityPages);
400      setCardPages(moduleJson.module.extensionAbilities);
401    }
402  }
403  return abilityPages;
404}
405
406function setEntrance(abilityConfig, abilityPages) {
407  if (abilityConfig && abilityConfig.length > 0) {
408    abilityConfig.forEach(ability => {
409      if (ability.srcEntry) {
410        abilityPages.push(ability.srcEntry)
411        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry))
412      } else if (ability.srcEntrance) {
413        abilityPages.push(ability.srcEntrance);
414        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance));
415      }
416    });
417  }
418}
419
420function setCardPages(extensionAbilities) {
421  if (extensionAbilities && extensionAbilities.length > 0) {
422    extensionAbilities.forEach(extensionAbility => {
423      if (extensionAbility.metadata) {
424        extensionAbility.metadata.forEach(metadata => {
425          if (metadata.resource) {
426            readCardResource(metadata.resource);
427          }
428        })
429      }
430    });
431  }
432}
433
434function readCardResource(resource) {
435  const cardJsonFileName = `${resource.replace(/\$profile\:/, '')}.json`;
436  const modulePagePath = path.resolve(projectConfig.aceProfilePath, cardJsonFileName);
437  if (fs.existsSync(modulePagePath)) {
438    const cardConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
439    if (cardConfig.forms) {
440      cardConfig.forms.forEach(form => {
441        readCardForm(form);
442      })
443    }
444  }
445}
446
447function readCardForm(form) {
448  if ((form.type && form.type === 'eTS') ||
449    (form.uiSyntax && form.uiSyntax === 'arkts')) {
450    const sourcePath = form.src.replace(/\.ets$/, '');
451    const cardPath = path.resolve(projectConfig.projectPath, '..', sourcePath + '.ets');
452    if (cardPath && fs.existsSync(cardPath)) {
453      projectConfig.entryObj['../' + sourcePath] = cardPath + '?entry';
454      projectConfig.cardEntryObj['../' + sourcePath] = cardPath;
455      projectConfig.cardObj[cardPath] = sourcePath.replace(/^\.\//, '');
456    }
457  }
458}
459
460function getAbilityFullPath(projectPath, abilityPath) {
461  const finalPath = path.resolve(path.resolve(projectPath, '../'), abilityPath);
462  if (fs.existsSync(finalPath)) {
463    return finalPath.toLowerCase();
464  } else {
465    return path.resolve(abilityPath).toLowerCase();
466  }
467}
468
469function loadWorker(projectConfig, workerFileEntry) {
470  if (workerFileEntry) {
471    projectConfig.entryObj = Object.assign(projectConfig.entryObj, workerFileEntry);
472  } else {
473    const workerPath = path.resolve(projectConfig.projectPath, WORKERS_DIR);
474    if (fs.existsSync(workerPath)) {
475      const workerFiles = [];
476      readFile(workerPath, workerFiles);
477      workerFiles.forEach((item) => {
478        if (/\.(ts|js)$/.test(item)) {
479          const relativePath = path.relative(workerPath, item)
480            .replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
481          projectConfig.entryObj[`./${WORKERS_DIR}/` + relativePath] = item;
482        }
483      });
484    }
485  }
486}
487
488let aceBuildJson = {};
489function loadBuildJson() {
490  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
491    aceBuildJson = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
492  }
493  if (aceBuildJson.packageManagerType === 'ohpm') {
494    projectConfig.packageManagerType = 'ohpm';
495    projectConfig.packageDir = 'oh_modules';
496    projectConfig.packageJson = 'oh-package.json5';
497  }
498}
499
500function initBuildInfo() {
501  projectConfig.projectRootPath = aceBuildJson.projectRootPath;
502  if (projectConfig.compileHar && aceBuildJson.moduleName &&
503    aceBuildJson.modulePathMap[aceBuildJson.moduleName]) {
504    projectConfig.moduleRootPath = aceBuildJson.modulePathMap[aceBuildJson.moduleName];
505  }
506}
507
508function readWorkerFile() {
509  const workerFileEntry = {};
510  if (aceBuildJson.workers) {
511    aceBuildJson.workers.forEach(worker => {
512      if (!/\.(ts|js)$/.test(worker)) {
513        worker += '.ts';
514      }
515      const relativePath = path.relative(projectConfig.projectPath, worker);
516      if (filterWorker(relativePath)) {
517        const workerKey = relativePath.replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
518        if (workerFileEntry[workerKey]) {
519          throw Error(
520            '\u001b[31m ERROR: The worker file cannot use the same file name: \n' +
521            workerFileEntry[workerKey] + '\n' + worker + '\u001b[39m'
522          ).message;
523        } else {
524          workerFileEntry[workerKey] = worker;
525        }
526      }
527    });
528    return workerFileEntry;
529  }
530  return null;
531}
532
533function readPatchConfig() {
534  if (aceBuildJson.patchConfig) {
535    projectConfig.hotReload = process.env.watchMode === 'true' && !projectConfig.isPreview;
536    projectConfig.patchAbcPath = aceBuildJson.patchConfig.patchAbcPath;
537    projectConfig.changedFileList = aceBuildJson.patchConfig.changedFileList ?
538      aceBuildJson.patchConfig.changedFileList : path.join(projectConfig.cachePath, 'changedFileList.json');
539    if (projectConfig.hotReload) {
540      writeFileSync(projectConfig.changedFileList, JSON.stringify({
541        modifiedFiles: [],
542        removedFiles: []
543      }));
544    }
545  }
546}
547
548function filterWorker(workerPath) {
549  return /\.(ts|js)$/.test(workerPath);
550}
551
552;(function initSystemResource() {
553  const sysResourcePath = path.resolve(__dirname, './sysResource.js');
554  if (fs.existsSync(sysResourcePath)) {
555    resources.sys = require(sysResourcePath).sys;
556  }
557})();
558
559;(function readSystemModules() {
560  const systemModulesPath = path.resolve(__dirname, '../../api');
561  if (fs.existsSync(systemModulesPath)) {
562    systemModules.push(...fs.readdirSync(systemModulesPath));
563  }
564})()
565
566function readAppResource(filePath) {
567  if (fs.existsSync(filePath)) {
568    const appResource = fs.readFileSync(filePath, "utf-8");
569    const resourceArr = appResource.split(/\n/);
570    let resourceMap = new Map();
571    processResourceArr(resourceArr, resourceMap, filePath);
572    for (let [key, value] of resourceMap) {
573      resources.app[key] = value;
574    }
575  }
576}
577
578function processResourceArr(resourceArr, resourceMap, filePath) {
579  for (let i = 0; i < resourceArr.length; i++) {
580    if (!resourceArr[i].length) {
581      continue;
582    }
583    const resourceData = resourceArr[i].split(/\s/);
584    if (resourceData.length === 3 && !isNaN(Number(resourceData[2])) ) {
585      if (resourceMap.get(resourceData[0])) {
586        const resourceKeys = resourceMap.get(resourceData[0]);
587        if (!resourceKeys[resourceData[1]] || resourceKeys[resourceData[1]] !== Number(resourceData[2])) {
588          resourceKeys[resourceData[1]] = Number(resourceData[2]);
589        }
590      } else {
591        let obj = {};
592        obj[resourceData[1]] = Number(resourceData[2]);
593        resourceMap.set(resourceData[0], obj);
594      }
595    } else {
596      logger.warn(`\u001b[31m ArkTS:WARN The format of file '${filePath}' is incorrect. \u001b[39m`);
597      break;
598    }
599  }
600}
601
602function hashProjectPath(projectPath) {
603  process.env.hashProjectPath = "_" + md5(projectPath);
604  return process.env.hashProjectPath;
605}
606
607function loadModuleInfo(projectConfig, envArgs) {
608  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
609    const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
610    if (buildJsonInfo.compileMode) {
611      projectConfig.compileMode = buildJsonInfo.compileMode;
612    }
613    projectConfig.projectRootPath = buildJsonInfo.projectRootPath;
614    projectConfig.modulePathMap = buildJsonInfo.modulePathMap;
615    projectConfig.isOhosTest = buildJsonInfo.isOhosTest;
616    let faultHandler = function (error) {
617      // rollup's error will be handled in fast build
618      if (process.env.compileTool === 'rollup') {
619        return;
620      }
621      logger.error(error);
622      process.exit(FAIL);
623    }
624    const compileMode = process.env.compileTool === 'rollup' ? projectConfig.compileMode : buildJsonInfo.compileMode;
625    if (checkAotConfig(compileMode, buildJsonInfo, faultHandler)) {
626      projectConfig.processTs = true;
627      projectConfig.pandaMode = TS2ABC;
628      projectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut;
629      projectConfig.anBuildMode = buildJsonInfo.anBuildMode;
630      projectConfig.apPath = buildJsonInfo.apPath;
631    } else {
632      projectConfig.processTs = false;
633      projectConfig.pandaMode = buildJsonInfo.pandaMode;
634    }
635    if (envArgs !== undefined) {
636      projectConfig.buildArkMode = envArgs.buildMode;
637    }
638    if (compileMode === 'esmodule') {
639      projectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath;
640      projectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap;
641    }
642    if (projectConfig.compileHar && buildJsonInfo.moduleName &&
643      buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]) {
644      projectConfig.moduleRootPath = buildJsonInfo.modulePathMap[buildJsonInfo.moduleName];
645    }
646  }
647}
648
649function checkAppResourcePath(appResourcePath, config) {
650  if (appResourcePath) {
651    readAppResource(appResourcePath);
652    if (fs.existsSync(appResourcePath) && config.cache) {
653      config.cache.buildDependencies.config.push(appResourcePath);
654    }
655    if (!projectConfig.xtsMode) {
656      const appResourcePathSavePath = path.resolve(projectConfig.cachePath, 'resource_path.txt');
657      saveAppResourcePath(appResourcePath, appResourcePathSavePath);
658      if (fs.existsSync(appResourcePathSavePath) && config.cache) {
659        config.cache.buildDependencies.config.push(appResourcePathSavePath);
660      }
661    }
662  }
663}
664
665function saveAppResourcePath(appResourcePath, appResourcePathSavePath) {
666  let isSave = false;
667  if (fs.existsSync(appResourcePathSavePath)) {
668    const saveContent = fs.readFileSync(appResourcePathSavePath);
669    if (appResourcePath !== saveContent) {
670      isSave = true;
671    }
672  } else {
673    isSave = true;
674  }
675  if (isSave) {
676    fs.writeFileSync(appResourcePathSavePath, appResourcePath);
677  }
678}
679
680function addSDKBuildDependencies(config) {
681  if (projectConfig.localPropertiesPath &&
682    fs.existsSync(projectConfig.localPropertiesPath) && config.cache) {
683    config.cache.buildDependencies.config.push(projectConfig.localPropertiesPath)
684  }
685  if (projectConfig.projectProfilePath &&
686    fs.existsSync(projectConfig.projectProfilePath) && config.cache) {
687    config.cache.buildDependencies.config.push(projectConfig.projectProfilePath)
688  }
689}
690
691function getCleanConfig(workerFile) {
692  const cleanPath = [];
693  if (projectConfig.compileMode === 'esmodule') {
694    return cleanPath;
695  }
696  cleanPath.push(projectConfig.buildPath);
697  if (workerFile) {
698    const workerFilesPath = Object.keys(workerFile);
699    for (const workerFilePath of workerFilesPath) {
700      cleanPath.push(path.join(projectConfig.buildPath, workerFilePath, '..'));
701    }
702  }
703  return cleanPath;
704}
705
706function isPartialUpdate(metadata) {
707  if (Array.isArray(metadata) && metadata.length) {
708    metadata.some(item => {
709      if (item.name && item.name === 'ArkTSPartialUpdate' &&
710        item.value && item.value === 'false') {
711        partialUpdateConfig.partialUpdateMode = false;
712      }
713      if (item.name && item.name === 'ArkTSBuilderCheck' &&
714        item.value && item.value === 'false') {
715        partialUpdateConfig.builderCheck = false;
716      }
717      return !partialUpdateConfig.partialUpdateMode && !partialUpdateConfig.builderCheck;
718    });
719  }
720}
721
722function partialUpdateController(minAPIVersion, metadata = null) {
723  if (minAPIVersion >= 9) {
724    partialUpdateConfig.partialUpdateMode = true;
725  }
726  if (metadata) {
727    isPartialUpdate(metadata);
728  }
729}
730
731const globalProgram = {
732  program: null,
733  watchProgram: null
734};
735
736const partialUpdateConfig = {
737  partialUpdateMode: false,
738  builderCheck: true
739};
740
741exports.globalProgram = globalProgram;
742exports.projectConfig = projectConfig;
743exports.loadEntryObj = loadEntryObj;
744exports.readAppResource = readAppResource;
745exports.resources = resources;
746exports.loadWorker = loadWorker;
747exports.abilityConfig = abilityConfig;
748exports.readWorkerFile = readWorkerFile;
749exports.abilityPagesFullPath = abilityPagesFullPath;
750exports.loadModuleInfo = loadModuleInfo;
751exports.systemModules = systemModules;
752exports.checkAppResourcePath = checkAppResourcePath;
753exports.addSDKBuildDependencies = addSDKBuildDependencies;
754exports.partialUpdateConfig = partialUpdateConfig;
755exports.readPatchConfig = readPatchConfig;
756exports.initBuildInfo = initBuildInfo;
757exports.getCleanConfig = getCleanConfig;
758