• 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 crypto = require('crypto');
19const JSON5 = require('json5');
20
21const {
22  readFile,
23  writeFileSync,
24  resourcesRawfile,
25  getStoredFileInfo
26} = require('./lib/utils');
27
28const {
29  COLD_RELOAD_MODE,
30  ES2ABC,
31  FAIL,
32  TEST_RUNNER_DIR_SET,
33  TS2ABC,
34  WORKERS_DIR
35} = require('./lib/pre_define');
36
37const {
38  checkAotConfig
39} = require('./lib/gen_aot');
40
41const {
42  configure,
43  getLogger
44} = require('log4js');
45
46configure({
47  appenders: { 'ETS': {type: 'stderr', layout: {type: 'messagePassThrough'}}},
48  categories: {'default': {appenders: ['ETS'], level: 'info'}}
49});
50const logger = getLogger('ETS');
51
52let staticPreviewPage = process.env.aceStaticPreview;
53let aceCompileMode = process.env.aceCompileMode || 'page';
54const abilityConfig = {
55  abilityType: process.env.abilityType || 'page',
56  abilityEntryFile: null,
57  projectAbilityPath: [],
58  testRunnerFile: []
59};
60const projectConfig = {};
61const resources = {
62  app: {},
63  sys: {}
64};
65const systemModules = [];
66const abilityPagesFullPath = [];
67let globalModulePaths = [];
68let sdkConfigs = [];
69let defaultSdkConfigs = [];
70let extendSdkConfigs = [];
71let sdkConfigPrefix = 'ohos|system|kit|arkts';
72let ohosSystemModulePaths = [];
73let ohosSystemModuleSubDirPaths = [];
74let allModulesPaths = [];
75
76function initProjectConfig(projectConfig) {
77  projectConfig.entryObj = {};
78  projectConfig.cardObj = {};
79  projectConfig.projectPath = projectConfig.projectPath || process.env.aceModuleRoot ||
80    path.join(process.cwd(), 'sample');
81  projectConfig.buildPath = projectConfig.buildPath || process.env.aceModuleBuild ||
82    path.resolve(projectConfig.projectPath, 'build');
83  projectConfig.aceModuleBuild = projectConfig.buildPath; // To be compatible with both webpack and rollup
84  projectConfig.manifestFilePath = projectConfig.manifestFilePath || process.env.aceManifestPath ||
85    path.join(projectConfig.projectPath, 'manifest.json');
86  projectConfig.aceProfilePath = projectConfig.aceProfilePath || process.env.aceProfilePath;
87  projectConfig.aceModuleJsonPath = projectConfig.aceModuleJsonPath || process.env.aceModuleJsonPath;
88  projectConfig.aceSuperVisualPath = projectConfig.aceSuperVisualPath ||
89    process.env.aceSuperVisualPath;
90  projectConfig.hashProjectPath = projectConfig.hashProjectPath ||
91    hashProjectPath(projectConfig.projectPath);
92  projectConfig.aceBuildJson = projectConfig.aceBuildJson || process.env.aceBuildJson;
93  projectConfig.cachePath = projectConfig.cachePath || process.env.cachePath ||
94    path.resolve(__dirname, 'node_modules/.cache');
95  projectConfig.aceSoPath = projectConfig.aceSoPath || process.env.aceSoPath;
96  projectConfig.xtsMode = /ets_loader_ark$/.test(__dirname);
97  projectConfig.localPropertiesPath = projectConfig.localPropertiesPath || process.env.localPropertiesPath;
98  projectConfig.projectProfilePath = projectConfig.projectProfilePath || process.env.projectProfilePath;
99  projectConfig.isPreview = projectConfig.isPreview || process.env.isPreview === 'true';
100  projectConfig.compileMode = projectConfig.compileMode || process.env.compileMode || 'jsbundle';
101  projectConfig.runtimeOS = projectConfig.runtimeOS || process.env.runtimeOS || 'default';
102  projectConfig.sdkInfo = projectConfig.sdkInfo || process.env.sdkInfo || 'default';
103  projectConfig.compileHar = false;
104  projectConfig.compileShared = false;
105  projectConfig.splitCommon = false;
106  projectConfig.checkEntry = projectConfig.checkEntry || process.env.checkEntry;
107  projectConfig.obfuscateHarType = projectConfig.obfuscateHarType || process.env.obfuscate;
108  projectConfig.packageDir = 'node_modules';
109  projectConfig.packageJson = 'package.json';
110  projectConfig.packageManagerType = 'npm';
111  projectConfig.cardEntryObj = {};
112  projectConfig.compilerTypes = [];
113  projectConfig.isCrossplatform = projectConfig.isCrossplatform || false;
114  projectConfig.enableDebugLine = projectConfig.enableDebugLine || process.env.enableDebugLine || false;
115  projectConfig.bundleType = projectConfig.bundleType || process.env.bundleType || '';
116  projectConfig.optLazyForEach = false;
117  projectConfig.hspResourcesMap = false;
118  projectConfig.useArkoala = false;
119  projectConfig.resetBundleName = false;
120  projectConfig.integratedHsp = false;
121  projectConfig.useTsHar = false;
122}
123
124function loadEntryObj(projectConfig) {
125  let manifest = {};
126  initMain();
127  initProjectConfig(projectConfig);
128  loadBuildJson();
129  if (process.env.aceManifestPath && aceCompileMode === 'page') {
130    setEntryFile(projectConfig);
131    setFaTestRunnerFile(projectConfig);
132  }
133  if (process.env.aceModuleJsonPath) {
134    setIntentEntryPages(projectConfig);
135    setAbilityPages(projectConfig);
136    setStageTestRunnerFile(projectConfig);
137    loadNavigationConfig(aceBuildJson);
138  }
139
140  if (staticPreviewPage) {
141    projectConfig.entryObj['./' + staticPreviewPage] = projectConfig.projectPath + path.sep +
142      staticPreviewPage + '.ets?entry';
143  } else if (abilityConfig.abilityType === 'page') {
144    if (fs.existsSync(projectConfig.manifestFilePath)) {
145      const jsonString = fs.readFileSync(projectConfig.manifestFilePath).toString();
146      manifest = JSON.parse(jsonString);
147      if (manifest && manifest.minPlatformVersion) {
148        process.env.minPlatformVersion = manifest.minPlatformVersion;
149        partialUpdateController(manifest.minPlatformVersion);
150      }
151      projectConfig.pagesJsonFileName = 'config.json';
152    } else if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
153      process.env.compileMode = 'moduleJson';
154      buildManifest(manifest, projectConfig.aceModuleJsonPath);
155    } else {
156      throw Error('\u001b[31m ERROR: the manifest file ' + projectConfig.manifestFilePath.replace(/\\/g, '/') +
157        ' or module.json is lost or format is invalid. \u001b[39m').message;
158    }
159    if (!projectConfig.compileHar) {
160      if (manifest.pages) {
161        const pages = manifest.pages;
162        pages.forEach((element) => {
163          const sourcePath = element.replace(/^\.\/ets\//, '');
164          const fileName = path.resolve(projectConfig.projectPath, sourcePath + '.ets');
165          if (fs.existsSync(fileName)) {
166            projectConfig.entryObj['./' + sourcePath] = fileName + '?entry';
167          } else {
168            throw Error(`\u001b[31m ERROR: page '${fileName.replace(/\\/g, '/')}' does not exist. \u001b[39m`)
169              .message;
170          }
171        });
172      } else {
173        throw Error('\u001b[31m ERROR: missing pages attribute in ' +
174          projectConfig.manifestFilePath.replace(/\\/g, '/') +
175          '. \u001b[39m').message;
176      }
177    }
178  }
179}
180
181function loadNavigationConfig(aceBuildJson) {
182  if (aceBuildJson && aceBuildJson.routerMap && Array.isArray(aceBuildJson.routerMap)) {
183    aceBuildJson.routerMap.forEach((item) => {
184      if (item.pageSourceFile && item.name && item.buildFunction) {
185        const filePath = path.resolve(item.pageSourceFile);
186        const storedFileInfo = getStoredFileInfo();
187        if (storedFileInfo.routerInfo.has(filePath)) {
188          storedFileInfo.routerInfo.get(filePath).push({name: item.name, buildFunction: item.buildFunction});
189        } else {
190          storedFileInfo.routerInfo.set(filePath, [{name: item.name, buildFunction: item.buildFunction}]);
191        }
192      }
193    });
194  }
195}
196
197function buildManifest(manifest, aceConfigPath) {
198  try {
199    const moduleConfigJson = JSON.parse(fs.readFileSync(aceConfigPath).toString());
200    manifest.type = process.env.abilityType;
201    if (moduleConfigJson && moduleConfigJson.app && moduleConfigJson.app.minAPIVersion) {
202      if (moduleConfigJson.module && moduleConfigJson.module.metadata) {
203        partialUpdateController(moduleConfigJson.app.minAPIVersion, moduleConfigJson.module.metadata,
204          moduleConfigJson.module.type);
205        stageOptimization(moduleConfigJson.module.metadata);
206      } else {
207        partialUpdateController(moduleConfigJson.app.minAPIVersion);
208      }
209    }
210    if (moduleConfigJson.module) {
211      switch (moduleConfigJson.module.type) {
212        case 'har':
213          projectConfig.compileHar = true;
214          getPackageJsonEntryPath();
215          break;
216        case 'shared':
217          projectConfig.compileShared = true;
218          getPackageJsonEntryPath();
219          manifest.pages = getPages(moduleConfigJson);
220          break;
221        default:
222          manifest.pages = getPages(moduleConfigJson);
223          break;
224      }
225    } else {
226      throw Error('\u001b[31m' +
227        'BUIDERROR: the config.json file miss key word module || module[abilities].' +
228        '\u001b[39m').message;
229    }
230  } catch (e) {
231    if (/BUIDERROR/.test(e)) {
232      throw Error(e.replace('BUIDERROR', 'ERROR')).message;
233    } else {
234      throw Error('\x1B[31m' + 'ERROR: the module.json file is lost or format is invalid.' +
235        '\x1B[39m').message;
236    }
237  }
238}
239
240function getPackageJsonEntryPath() {
241  const rootPackageJsonPath = path.resolve(projectConfig.projectPath, '../../../' + projectConfig.packageJson);
242  if (fs.existsSync(rootPackageJsonPath)) {
243    let rootPackageJsonContent;
244    try {
245      rootPackageJsonContent = (projectConfig.packageManagerType === 'npm' ?
246        JSON : JSON5).parse(fs.readFileSync(rootPackageJsonPath, 'utf-8'));
247    } catch (e) {
248      throw Error('\u001b[31m' + 'BUIDERROR: ' + rootPackageJsonPath + ' format is invalid.' + '\u001b[39m').message;
249    }
250    if (rootPackageJsonContent) {
251      if (rootPackageJsonContent.module) {
252        getEntryPath(rootPackageJsonContent.module, rootPackageJsonPath);
253      } else if (rootPackageJsonContent.main) {
254        getEntryPath(rootPackageJsonContent.main, rootPackageJsonPath);
255      } else {
256        getEntryPath('', rootPackageJsonPath);
257      }
258    } else if (projectConfig.compileHar) {
259      throw Error('\u001b[31m' + 'BUIDERROR: lack message in ' + projectConfig.packageJson + '.' +
260        '\u001b[39m').message;
261    }
262  }
263}
264
265function supportSuffix(mainEntryPath) {
266  if (fs.existsSync(path.join(mainEntryPath, 'index.ets'))) {
267    mainEntryPath = path.join(mainEntryPath, 'index.ets');
268  } else if (fs.existsSync(path.join(mainEntryPath, 'index.ts'))) {
269    mainEntryPath = path.join(mainEntryPath, 'index.ts');
270  } else if (fs.existsSync(path.join(mainEntryPath, 'index.js'))) {
271    mainEntryPath = path.join(mainEntryPath, 'index.js');
272  } else if (projectConfig.compileHar) {
273    throw Error('\u001b[31m' + 'BUIDERROR: not find entry file in ' + projectConfig.packageJson +
274      '.' + '\u001b[39m').message;
275  }
276  return mainEntryPath;
277}
278
279function supportExtName(mainEntryPath) {
280  if (path.extname(mainEntryPath) === '') {
281    if (fs.existsSync(mainEntryPath + '.ets')) {
282      mainEntryPath = mainEntryPath + '.ets';
283    } else if (fs.existsSync(mainEntryPath + '.ts')) {
284      mainEntryPath = mainEntryPath + '.ts';
285    } else if (fs.existsSync(mainEntryPath + '.js')) {
286      mainEntryPath = mainEntryPath + '.js';
287    }
288  }
289  return mainEntryPath;
290}
291
292function getEntryPath(entryPath, rootPackageJsonPath) {
293  let mainEntryPath = path.resolve(rootPackageJsonPath, '../', entryPath);
294  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isDirectory()) {
295    mainEntryPath = supportSuffix(mainEntryPath);
296  } else {
297    mainEntryPath = supportExtName(mainEntryPath);
298  }
299  if (fs.existsSync(mainEntryPath) && fs.statSync(mainEntryPath).isFile()) {
300    const entryKey = path.relative(projectConfig.projectPath, mainEntryPath);
301    projectConfig.entryObj[entryKey] = mainEntryPath;
302    abilityPagesFullPath.push(path.resolve(mainEntryPath).toLowerCase());
303  } else if (projectConfig.compileHar) {
304    throw Error('\u001b[31m' + `BUIDERROR: not find entry file in ${rootPackageJsonPath}.` + '\u001b[39m').message;
305  }
306}
307
308function stageOptimization(metadata) {
309  if (Array.isArray(metadata) && metadata.length) {
310    metadata.some(item => {
311      if (item.name && item.name === 'USE_COMMON_CHUNK' &&
312        item.value && item.value === 'true') {
313        projectConfig.splitCommon = true;
314        return true;
315      }
316    });
317  }
318}
319
320function getPages(configJson) {
321  const pages = [];
322  let pagesJsonFileName = '';
323  // pages is not necessary in stage
324  if (process.env.compileMode === 'moduleJson' && configJson.module && configJson.module.pages) {
325    pagesJsonFileName = `${configJson.module.pages.replace(/\$profile\:/, '')}.json`;
326  } else {
327    return pages;
328  }
329  const modulePagePath = path.resolve(projectConfig.aceProfilePath, pagesJsonFileName);
330  if (fs.existsSync(modulePagePath)) {
331    try {
332      const pagesConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
333      if (pagesConfig && pagesConfig.src) {
334        projectConfig.pagesJsonFileName = pagesJsonFileName;
335        return pagesConfig.src;
336      }
337    } catch (e) {
338      throw Error('\x1B[31m' + `BUIDERROR: the ${modulePagePath} file format is invalid.` +
339        '\x1B[39m').message;
340    }
341  }
342  return pages;
343}
344
345function setEntryFile(projectConfig) {
346  const entryFileName = abilityConfig.abilityType === 'page' ? 'app' : abilityConfig.abilityType;
347  const extendFile = entryFileName === 'app' ? '.ets' : '.ts';
348  const entryFileRealPath = entryFileName + extendFile;
349  const entryFilePath = path.resolve(projectConfig.projectPath, entryFileRealPath);
350  abilityConfig.abilityEntryFile = entryFilePath;
351  if (!fs.existsSync(entryFilePath) && aceCompileMode === 'page') {
352    throw Error(`\u001b[31m ERROR: missing ${entryFilePath.replace(/\\/g, '/')}. \u001b[39m`).message;
353  }
354  projectConfig.entryObj[`./${entryFileName}`] = entryFilePath + '?entry';
355}
356
357function setIntentEntryPages(projectConfig) {
358  projectConfig.intentEntry.forEach(pages => {
359    const entryKey = path.relative(projectConfig.projectPath, pages).replace(/\.(ets|ts|js)$/, '');
360    projectConfig.entryObj[entryKey] = pages;
361    if (/\.ets$/.test(pages)) {
362      abilityPagesFullPath.push(path.resolve(pages).toLowerCase());
363    }
364  });
365}
366
367function setAbilityPages(projectConfig) {
368  let abilityPages = [];
369  if (projectConfig.aceModuleJsonPath && fs.existsSync(projectConfig.aceModuleJsonPath)) {
370    const moduleJson = JSON.parse(fs.readFileSync(projectConfig.aceModuleJsonPath).toString());
371    abilityPages = readAbilityEntrance(moduleJson);
372    setAbilityFile(projectConfig, abilityPages);
373    setBundleModuleInfo(projectConfig, moduleJson);
374  }
375}
376
377function setTestRunnerFile(projectConfig, isStageBased) {
378  const index = projectConfig.projectPath.split(path.sep).join('/').lastIndexOf('\/');
379  TEST_RUNNER_DIR_SET.forEach((dir) => {
380    const projectPath = isStageBased ? projectConfig.projectPath : projectConfig.projectPath.substring(0, index + 1);
381    const testRunnerPath = path.resolve(projectPath, dir);
382    if (fs.existsSync(testRunnerPath) && folderExistsCaseSensitive(testRunnerPath)) {
383      const testRunnerFiles = [];
384      readFile(testRunnerPath, testRunnerFiles);
385      testRunnerFiles.forEach((item) => {
386        if (/\.(ts|js|ets)$/.test(item)) {
387          if (/\.ets$/.test(item)) {
388            abilityPagesFullPath.push(path.resolve(item).toLowerCase());
389          }
390          const relativePath = path.relative(testRunnerPath, item).replace(/\.(ts|js|ets)$/, '');
391          if (isStageBased) {
392            projectConfig.entryObj[`./${dir}/${relativePath}`] = item;
393          } else {
394            projectConfig.entryObj[`../${dir}/${relativePath}`] = item;
395          }
396          abilityConfig.testRunnerFile.push(item);
397        }
398      });
399    }
400  });
401}
402
403function folderExistsCaseSensitive(folderPath) {
404  try {
405    const folders = fs.readdirSync(path.dirname(folderPath), { withFileTypes: true })
406      .filter(entry => entry.isDirectory())
407      .map(entry => entry.name);
408    const targetFolderName = path.basename(folderPath);
409    return folders.includes(targetFolderName);
410  } catch (error) {
411    return false;
412  }
413}
414
415function setFaTestRunnerFile(projectConfig) {
416  setTestRunnerFile(projectConfig, false);
417}
418
419function setStageTestRunnerFile(projectConfig) {
420  setTestRunnerFile(projectConfig, true);
421}
422
423function setBundleModuleInfo(projectConfig, moduleJson) {
424  if (moduleJson.module) {
425    projectConfig.moduleName = moduleJson.module.name;
426  }
427  if (moduleJson.app) {
428    projectConfig.bundleName = moduleJson.app.bundleName;
429  }
430}
431
432function setAbilityFile(projectConfig, abilityPages) {
433  abilityPages.forEach(abilityPath => {
434    const projectAbilityPath = path.resolve(projectConfig.projectPath, '../', abilityPath);
435    if (path.isAbsolute(abilityPath)) {
436      abilityPath = '.' + abilityPath.slice(projectConfig.projectPath.length);
437    }
438    const entryPageKey = abilityPath.replace(/^\.\/ets\//, './').replace(/\.ts$/, '').replace(/\.ets$/, '');
439    if (fs.existsSync(projectAbilityPath)) {
440      abilityConfig.projectAbilityPath.push(projectAbilityPath);
441      projectConfig.entryObj[entryPageKey] = projectAbilityPath + '?entry';
442    } else {
443      throw Error(
444        `\u001b[31m ERROR: srcEntry file '${projectAbilityPath.replace(/\\/g, '/')}' does not exist. \u001b[39m`
445      ).message;
446    }
447  });
448}
449
450function readAbilityEntrance(moduleJson) {
451  const abilityPages = [];
452  if (moduleJson.module) {
453    const moduleSrcEntrance = moduleJson.module.srcEntrance;
454    const moduleSrcEntry = moduleJson.module.srcEntry;
455    if (moduleSrcEntry) {
456      abilityPages.push(moduleSrcEntry);
457      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntry));
458    } else if (moduleSrcEntrance) {
459      abilityPages.push(moduleSrcEntrance);
460      abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, moduleSrcEntrance));
461    }
462    if (moduleJson.module.abilities && moduleJson.module.abilities.length > 0) {
463      setEntrance(moduleJson.module.abilities, abilityPages);
464    }
465    if (moduleJson.module.extensionAbilities && moduleJson.module.extensionAbilities.length > 0) {
466      setEntrance(moduleJson.module.extensionAbilities, abilityPages);
467      setCardPages(moduleJson.module.extensionAbilities);
468    }
469  }
470  return abilityPages;
471}
472
473function setEntrance(abilityConfig, abilityPages) {
474  if (abilityConfig && abilityConfig.length > 0) {
475    abilityConfig.forEach(ability => {
476      if (ability.srcEntry) {
477        abilityPages.push(ability.srcEntry);
478        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntry));
479      } else if (ability.srcEntrance) {
480        abilityPages.push(ability.srcEntrance);
481        abilityPagesFullPath.push(getAbilityFullPath(projectConfig.projectPath, ability.srcEntrance));
482      }
483    });
484  }
485}
486
487function setCardPages(extensionAbilities) {
488  if (extensionAbilities && extensionAbilities.length > 0) {
489    extensionAbilities.forEach(extensionAbility => {
490      if (extensionAbility.metadata) {
491        extensionAbility.metadata.forEach(metadata => {
492          if (metadata.resource) {
493            readCardResource(metadata.resource);
494          }
495        });
496      }
497    });
498  }
499}
500
501function readCardResource(resource) {
502  const cardJsonFileName = `${resource.replace(/\$profile\:/, '')}.json`;
503  const modulePagePath = path.resolve(projectConfig.aceProfilePath, cardJsonFileName);
504  if (fs.existsSync(modulePagePath)) {
505    const cardConfig = JSON.parse(fs.readFileSync(modulePagePath, 'utf-8'));
506    if (cardConfig.forms) {
507      cardConfig.forms.forEach(form => {
508        readCardForm(form);
509      });
510    }
511  }
512}
513
514function readCardForm(form) {
515  if ((form.type && form.type === 'eTS') ||
516    (form.uiSyntax && form.uiSyntax === 'arkts')) {
517    const sourcePath = form.src.replace(/\.ets$/, '');
518    const cardPath = path.resolve(projectConfig.projectPath, '..', sourcePath + '.ets');
519    if (cardPath && fs.existsSync(cardPath)) {
520      projectConfig.entryObj['../' + sourcePath] = cardPath + '?entry';
521      projectConfig.cardEntryObj['../' + sourcePath] = cardPath;
522      projectConfig.cardObj[cardPath] = sourcePath.replace(/^\.\//, '');
523    }
524  }
525}
526
527function getAbilityFullPath(projectPath, abilityPath) {
528  const finalPath = path.resolve(path.resolve(projectPath, '../'), abilityPath);
529  if (fs.existsSync(finalPath)) {
530    return finalPath.toLowerCase();
531  } else {
532    return path.resolve(abilityPath).toLowerCase();
533  }
534}
535
536function loadWorker(projectConfig, workerFileEntry) {
537  if (workerFileEntry) {
538    projectConfig.entryObj = Object.assign(projectConfig.entryObj, workerFileEntry);
539  } else {
540    const workerPath = path.resolve(projectConfig.projectPath, WORKERS_DIR);
541    if (fs.existsSync(workerPath)) {
542      const workerFiles = [];
543      readFile(workerPath, workerFiles);
544      workerFiles.forEach((item) => {
545        if (/\.(ts|js|ets)$/.test(item)) {
546          const relativePath = path.relative(workerPath, item)
547            .replace(/\.(ts|js|ets)$/, '').replace(/\\/g, '/');
548          projectConfig.entryObj[`./${WORKERS_DIR}/` + relativePath] = item;
549          abilityPagesFullPath.push(path.resolve(item).toLowerCase());
550        }
551      });
552    }
553  }
554}
555
556let aceBuildJson = {};
557function loadBuildJson() {
558  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
559    aceBuildJson = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
560  }
561  if (aceBuildJson.packageManagerType === 'ohpm') {
562    projectConfig.packageManagerType = 'ohpm';
563    projectConfig.packageDir = 'oh_modules';
564    projectConfig.packageJson = 'oh-package.json5';
565  }
566  // add intent framework entry file
567  projectConfig.intentEntry = aceBuildJson.compileEntry || [];
568}
569
570function initBuildInfo() {
571  projectConfig.projectRootPath = aceBuildJson.projectRootPath;
572  if (projectConfig.compileHar && aceBuildJson.moduleName &&
573    aceBuildJson.modulePathMap[aceBuildJson.moduleName]) {
574    projectConfig.moduleRootPath = aceBuildJson.modulePathMap[aceBuildJson.moduleName];
575  }
576}
577
578function readWorkerFile() {
579  const workerFileEntry = {};
580  if (aceBuildJson.workers) {
581    aceBuildJson.workers.forEach(worker => {
582      if (!/\.(ts|js|ets)$/.test(worker)) {
583        throw Error(
584          '\u001b[31mArkTS:ERROR: File: ' + worker + '.' + '\n' +
585          "  The worker file can only be an '.ets', '.ts', or '.js' file.\u001b[39m"
586        ).message;
587      }
588      const relativePath = path.relative(projectConfig.projectPath, worker);
589      if (filterWorker(relativePath)) {
590        const workerKey = relativePath.replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
591        if (workerFileEntry[workerKey]) {
592          throw Error(
593            '\u001b[31m ERROR: The worker file cannot use the same file name: \n' +
594            workerFileEntry[workerKey] + '\n' + worker + '\u001b[39m'
595          ).message;
596        } else {
597          workerFileEntry[workerKey] = worker;
598          abilityPagesFullPath.push(path.resolve(workerFileEntry[workerKey]).toLowerCase());
599        }
600      }
601    });
602    return workerFileEntry;
603  }
604  return null;
605}
606
607function readPatchConfig() {
608  if (aceBuildJson.patchConfig) {
609    projectConfig.hotReload = process.env.watchMode === 'true' && !projectConfig.isPreview;
610    projectConfig.coldReload = aceBuildJson.patchConfig.mode === COLD_RELOAD_MODE ? true : false;
611    // The "isFirstBuild" field indicates whether it is the first compilation of cold reload mode
612    // It is determined by hvigor and passed via env
613    projectConfig.isFirstBuild = process.env.isFirstBuild === 'false' ? false : true;
614    projectConfig.patchAbcPath = aceBuildJson.patchConfig.patchAbcPath;
615    projectConfig.changedFileList = aceBuildJson.patchConfig.changedFileList ?
616      aceBuildJson.patchConfig.changedFileList : path.join(projectConfig.cachePath, 'changedFileList.json');
617    if (projectConfig.hotReload) {
618      writeFileSync(projectConfig.changedFileList, JSON.stringify({
619        modifiedFiles: [],
620        removedFiles: []
621      }));
622    }
623  }
624}
625
626function filterWorker(workerPath) {
627  return /\.(ts|js|ets)$/.test(workerPath);
628}
629
630;(function initSystemResource() {
631  const sysResourcePath = path.resolve(__dirname, './sysResource.js');
632  if (fs.existsSync(sysResourcePath)) {
633    resources.sys = require(sysResourcePath).sys;
634  }
635  if (process.env.externalApiPaths) {
636    const sysResourceExtPath = path.resolve(__dirname, process.env.externalApiPaths, 'sysResource.js');
637    if (fs.existsSync(sysResourceExtPath)) {
638      Object.entries(require(sysResourceExtPath).sys).forEach(([key, value]) => {
639        if (key in resources.sys) {
640          Object.assign(resources.sys[key], value);
641        }
642      });
643    }
644  }
645})();
646
647;(function readSystemModules() {
648  const apiDirPath = path.resolve(__dirname, '../../api');
649  const arktsDirPath = path.resolve(__dirname, '../../arkts');
650  const kitsDirPath = path.resolve(__dirname, '../../kits');
651  const systemModulePathArray = [apiDirPath];
652  if (!process.env.isFaMode) {
653    systemModulePathArray.push(arktsDirPath, kitsDirPath);
654  }
655  systemModulePathArray.forEach(systemModulesPath => {
656    if (fs.existsSync(systemModulesPath)) {
657      globalModulePaths.push(systemModulesPath);
658      const modulePaths = [];
659      readFile(systemModulesPath, modulePaths);
660      systemModules.push(...fs.readdirSync(systemModulesPath));
661      ohosSystemModulePaths.push(...modulePaths);
662      const moduleSubdir = modulePaths.filter(filePath => {
663        const dirName = path.dirname(filePath);
664        return !(dirName === apiDirPath || dirName === arktsDirPath || dirName === kitsDirPath);
665      }).map(filePath => {
666        return filePath
667          .replace(apiDirPath, '')
668          .replace(arktsDirPath, '')
669          .replace(kitsDirPath, '')
670          .replace(/(^\\)|(.d.e?ts$)/g, '')
671          .replace(/\\/g, '/');
672      });
673      ohosSystemModuleSubDirPaths.push(...moduleSubdir);
674      allModulesPaths.push(...modulePaths);
675    }
676  });
677  defaultSdkConfigs = [
678    {
679      'apiPath': systemModulePathArray,
680      'prefix': '@ohos'
681    }, {
682      'apiPath': systemModulePathArray,
683      'prefix': '@system'
684    }, {
685      'apiPath': systemModulePathArray,
686      'prefix': '@arkts'
687    }
688  ];
689  const externalApiPathStr = process.env.externalApiPaths || '';
690  const externalApiPaths = externalApiPathStr.split(path.delimiter);
691  collectExternalModules(externalApiPaths);
692  sdkConfigs = [...defaultSdkConfigs, ...extendSdkConfigs];
693})();
694
695function collectExternalModules(sdkPaths) {
696  for (let i = 0; i < sdkPaths.length; i++) {
697    const sdkPath = sdkPaths[i];
698    const sdkConfigPath = path.resolve(sdkPath, 'sdkConfig.json');
699    if (!fs.existsSync(sdkConfigPath)) {
700      continue;
701    }
702    const sdkConfig = JSON.parse(fs.readFileSync(sdkConfigPath, 'utf-8'));
703    if (!sdkConfig.apiPath) {
704      continue;
705    }
706    let externalApiPathArray = [];
707    if (Array.isArray(sdkConfig.apiPath)) {
708      externalApiPathArray = sdkConfig.apiPath;
709    } else {
710      externalApiPathArray.push(sdkConfig.apiPath);
711    }
712    const resolveApiPathArray = [];
713    externalApiPathArray.forEach(element => {
714      const resolvePath = path.resolve(sdkPath, element);
715      resolveApiPathArray.push(resolvePath);
716      if (fs.existsSync(resolvePath)) {
717        const extrenalModulePaths = [];
718        globalModulePaths.push(resolvePath);
719        systemModules.push(...fs.readdirSync(resolvePath));
720        readFile(resolvePath, extrenalModulePaths);
721        allModulesPaths.push(...extrenalModulePaths);
722      }
723    });
724    sdkConfigPrefix += `|${sdkConfig.prefix.replace(/^@/, '')}`;
725    sdkConfig.apiPath = resolveApiPathArray;
726    extendSdkConfigs.push(sdkConfig);
727  }
728}
729
730function readHspResource() {
731  if (aceBuildJson.hspResourcesMap) {
732    projectConfig.hspResourcesMap = true;
733    for (const hspName in aceBuildJson.hspResourcesMap) {
734      if (fs.existsSync(aceBuildJson.hspResourcesMap[hspName])) {
735        const resourceMap = new Map();
736        const hspResourceCollect = resources[hspName] = {};
737        const hspResource = fs.readFileSync(aceBuildJson.hspResourcesMap[hspName], 'utf-8');
738        const resourceArr = hspResource.split(/\n/);
739        processResourceArr(resourceArr, resourceMap, aceBuildJson.hspResourcesMap[hspName]);
740        for (const [key, value] of resourceMap) {
741          hspResourceCollect[key] = value;
742        }
743      }
744    }
745  }
746}
747
748function readAppResource(filePath) {
749  readHspResource();
750  if (fs.existsSync(filePath)) {
751    const appResource = fs.readFileSync(filePath, 'utf-8');
752    const resourceArr = appResource.split(/\n/);
753    const resourceMap = new Map();
754    processResourceArr(resourceArr, resourceMap, filePath);
755    for (let [key, value] of resourceMap) {
756      resources.app[key] = value;
757    }
758  }
759  if (process.env.rawFileResource && process.env.compileMode === 'moduleJson') {
760    resourcesRawfile(process.env.rawFileResource, getStoredFileInfo().resourcesArr);
761  }
762}
763
764function processResourceArr(resourceArr, resourceMap, filePath) {
765  for (let i = 0; i < resourceArr.length; i++) {
766    if (!resourceArr[i].length) {
767      continue;
768    }
769    const resourceData = resourceArr[i].split(/\s/);
770    if (resourceData.length === 3 && !isNaN(Number(resourceData[2]))) {
771      if (resourceMap.get(resourceData[0])) {
772        const resourceKeys = resourceMap.get(resourceData[0]);
773        if (!resourceKeys[resourceData[1]] || resourceKeys[resourceData[1]] !== Number(resourceData[2])) {
774          resourceKeys[resourceData[1]] = Number(resourceData[2]);
775        }
776      } else {
777        let obj = {};
778        obj[resourceData[1]] = Number(resourceData[2]);
779        resourceMap.set(resourceData[0], obj);
780      }
781      if (process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') {
782        getStoredFileInfo().updateResourceList(resourceData[0] + '_' + resourceData[1]);
783      }
784    } else {
785      logger.warn(`\u001b[31m ArkTS:WARN The format of file '${filePath}' is incorrect. \u001b[39m`);
786      break;
787    }
788  }
789}
790
791function hashProjectPath(projectPath) {
792  const hash = crypto.createHash('sha256');
793  hash.update(projectPath.toString());
794  process.env.hashProjectPath = '_' + hash.digest('hex');
795  return process.env.hashProjectPath;
796}
797
798function loadModuleInfo(projectConfig, envArgs) {
799  if (projectConfig.aceBuildJson && fs.existsSync(projectConfig.aceBuildJson)) {
800    const buildJsonInfo = JSON.parse(fs.readFileSync(projectConfig.aceBuildJson).toString());
801    if (buildJsonInfo.compileMode) {
802      projectConfig.compileMode = buildJsonInfo.compileMode;
803    }
804    projectConfig.projectRootPath = buildJsonInfo.projectRootPath;
805    projectConfig.modulePathMap = buildJsonInfo.modulePathMap;
806    projectConfig.isOhosTest = buildJsonInfo.isOhosTest;
807    let faultHandler = function (error) {
808      // rollup's error will be handled in fast build
809      if (process.env.compileTool === 'rollup') {
810        return;
811      }
812      logger.error(error);
813      process.exit(FAIL);
814    };
815    projectConfig.es2abcCompileTsInAotMode = true;
816    projectConfig.es2abcCompileTsInNonAotMode = false;
817    const compileMode = process.env.compileTool === 'rollup' ? projectConfig.compileMode : buildJsonInfo.compileMode;
818    if (checkAotConfig(compileMode, buildJsonInfo, faultHandler)) {
819      projectConfig.processTs = true;
820      projectConfig.pandaMode = TS2ABC;
821      projectConfig.anBuildOutPut = buildJsonInfo.anBuildOutPut;
822      projectConfig.anBuildMode = buildJsonInfo.anBuildMode;
823      projectConfig.apPath = buildJsonInfo.apPath;
824      if (projectConfig.es2abcCompileTsInAotMode) {
825        projectConfig.pandaMode = ES2ABC;
826      }
827    } else {
828      projectConfig.processTs = false;
829      projectConfig.pandaMode = buildJsonInfo.pandaMode;
830      if (projectConfig.es2abcCompileTsInNonAotMode) {
831        projectConfig.pandaMode = ES2ABC;
832        projectConfig.processTs = true;
833      }
834    }
835    if (envArgs !== undefined) {
836      projectConfig.buildArkMode = envArgs.buildMode;
837    }
838    if (compileMode === 'esmodule') {
839      projectConfig.nodeModulesPath = buildJsonInfo.nodeModulesPath;
840      projectConfig.harNameOhmMap = buildJsonInfo.harNameOhmMap;
841    }
842    if (projectConfig.compileHar && buildJsonInfo.moduleName &&
843      buildJsonInfo.modulePathMap[buildJsonInfo.moduleName]) {
844      if (projectConfig.useTsHar) {
845        projectConfig.processTs = true;
846      }
847      projectConfig.moduleRootPath = buildJsonInfo.modulePathMap[buildJsonInfo.moduleName];
848    }
849  }
850}
851
852function checkAppResourcePath(appResourcePath, config) {
853  if (appResourcePath) {
854    readAppResource(appResourcePath);
855    if (fs.existsSync(appResourcePath) && config.cache) {
856      config.cache.buildDependencies.config.push(appResourcePath);
857    }
858    if (!projectConfig.xtsMode) {
859      const appResourcePathSavePath = path.resolve(projectConfig.cachePath, 'resource_path.txt');
860      saveAppResourcePath(appResourcePath, appResourcePathSavePath);
861      if (fs.existsSync(appResourcePathSavePath) && config.cache) {
862        config.cache.buildDependencies.config.push(appResourcePathSavePath);
863      }
864    }
865  }
866}
867
868function saveAppResourcePath(appResourcePath, appResourcePathSavePath) {
869  let isSave = false;
870  if (fs.existsSync(appResourcePathSavePath)) {
871    const saveContent = fs.readFileSync(appResourcePathSavePath);
872    if (appResourcePath !== saveContent) {
873      isSave = true;
874    }
875  } else {
876    isSave = true;
877  }
878  if (isSave) {
879    fs.writeFileSync(appResourcePathSavePath, appResourcePath);
880  }
881}
882
883function addSDKBuildDependencies(config) {
884  if (projectConfig.localPropertiesPath &&
885    fs.existsSync(projectConfig.localPropertiesPath) && config.cache) {
886    config.cache.buildDependencies.config.push(projectConfig.localPropertiesPath);
887  }
888  if (projectConfig.projectProfilePath &&
889    fs.existsSync(projectConfig.projectProfilePath) && config.cache) {
890    config.cache.buildDependencies.config.push(projectConfig.projectProfilePath);
891  }
892}
893
894function getCleanConfig(workerFile) {
895  const cleanPath = [];
896  if (projectConfig.compileMode === 'esmodule') {
897    return cleanPath;
898  }
899  cleanPath.push(projectConfig.buildPath);
900  if (workerFile) {
901    const workerFilesPath = Object.keys(workerFile);
902    for (const workerFilePath of workerFilesPath) {
903      cleanPath.push(path.join(projectConfig.buildPath, workerFilePath, '..'));
904    }
905  }
906  return cleanPath;
907}
908
909function isPartialUpdate(metadata, moduleType) {
910  if (!Array.isArray(metadata) || !metadata.length) {
911    return;
912  }
913  metadata.some(item => {
914    if (item.name && item.value) {
915      if (item.name === 'ArkTSPartialUpdate' && item.value === 'false') {
916        partialUpdateConfig.partialUpdateMode = false;
917        if (projectConfig.aceModuleJsonPath) {
918          logger.warn('\u001b[33m ArkTS:WARN File: ' + projectConfig.aceModuleJsonPath + '.' + '\n' +
919          " The 'ArkTSPartialUpdate' field will no longer be supported in the future. \u001b[39m");
920        }
921      }
922      if (item.name === 'ArkTSBuilderCheck' && item.value === 'false') {
923        partialUpdateConfig.builderCheck = false;
924      }
925      if (item.name === 'Api11ArkTSCheck' && item.value === 'SkipArkTSCheckInApi11') {
926        partialUpdateConfig.executeArkTSLinter = false;
927      }
928      if (item.name === 'Api11ArkTSCheckMode' && item.value === 'DoArkTSCheckInCompatibleModeInApi11') {
929        partialUpdateConfig.standardArkTSLinter = false;
930      }
931      if (item.name === 'ArkTSVersion') {
932        partialUpdateConfig.arkTSVersion = item.value;
933      }
934      if (item.name === 'OPTLazyForEach' && item.value === 'true') {
935        projectConfig.optLazyForEach = true;
936      }
937      if (item.name === 'SkipTscOhModuleCheck' && item.value === 'true') {
938        partialUpdateConfig.skipTscOhModuleCheck = true;
939      }
940      if (item.name === 'SkipArkTSStaticBlocksCheck' && item.value === 'true') {
941        partialUpdateConfig.skipArkTSStaticBlocksCheck = true;
942      }
943      if (item.name === 'ArkoalaPlugin' && item.value === 'true') {
944        projectConfig.useArkoala = true;
945      }
946      if (item.name === 'UseTsHar' && item.value === 'true' && moduleType === 'har') {
947        projectConfig.useTsHar = true;
948      }
949    }
950    return !partialUpdateConfig.partialUpdateMode && !partialUpdateConfig.builderCheck &&
951      !partialUpdateConfig.executeArkTSLinter && !partialUpdateConfig.standardArkTSLinter &&
952      partialUpdateConfig.arkTSVersion !== undefined && projectConfig.optLazyForEach &&
953      partialUpdateConfig.skipTscOhModuleCheck && partialUpdateConfig.skipArkTSStaticBlocksCheck;
954  });
955}
956
957function applicationConfig() {
958  const localProperties = path.resolve(aceBuildJson.projectRootPath, 'local.properties');
959  if (fs.existsSync(localProperties)) {
960    try {
961      const localPropertiesFile = fs.readFileSync(localProperties, {encoding: 'utf-8'}).split(/\r?\n/);
962      localPropertiesFile.some((item) => {
963        const builderCheckValue = item.replace(/\s+|;/g, '');
964        if (builderCheckValue === 'ArkTSConfig.ArkTSBuilderCheck=false') {
965          partialUpdateConfig.builderCheck = false;
966          return true;
967        }
968      });
969    } catch (err) {
970    }
971  }
972}
973
974function partialUpdateController(minAPIVersion, metadata = null, moduleType = '') {
975  projectConfig.minAPIVersion = minAPIVersion;
976  if (minAPIVersion >= 9) {
977    partialUpdateConfig.partialUpdateMode = true;
978  }
979  const MIN_VERSION_OPTIMIZE_COMPONENT = 10;
980  if (minAPIVersion < MIN_VERSION_OPTIMIZE_COMPONENT) {
981    partialUpdateConfig.optimizeComponent = false;
982  }
983  if (metadata) {
984    isPartialUpdate(metadata, moduleType);
985  }
986  if (aceBuildJson.projectRootPath) {
987    applicationConfig();
988  }
989}
990
991const globalProgram = {
992  builderProgram: null,
993  program: null,
994  watchProgram: null,
995  checker: null,
996  strictLanguageService: null,
997};
998
999const partialUpdateConfig = {
1000  partialUpdateMode: false,
1001  builderCheck: true,
1002  executeArkTSLinter: true,
1003  standardArkTSLinter: true,
1004  optimizeComponent: true,
1005  arkTSVersion: undefined,
1006  skipTscOhModuleCheck: false,
1007  skipArkTSStaticBlocksCheck: false,
1008};
1009
1010function resetMain() {
1011  staticPreviewPage = undefined;
1012  aceCompileMode = 'page';
1013  resetAbilityConfig();
1014  resetProjectConfig();
1015  resources.app = {};
1016  abilityPagesFullPath.length = 0;
1017  aceBuildJson = {};
1018  resetGlobalProgram();
1019  partialUpdateConfig.builderCheck = true;
1020  globalModulePaths = [];
1021  sdkConfigs = [];
1022  defaultSdkConfigs = [];
1023  extendSdkConfigs = [];
1024  sdkConfigPrefix = 'ohos|system|kit';
1025  ohosSystemModulePaths = [];
1026  ohosSystemModuleSubDirPaths = [];
1027  allModulesPaths = [];
1028}
1029
1030function resetAbilityConfig() {
1031  abilityConfig.abilityType = 'page';
1032  abilityConfig.abilityEntryFile = null;
1033  abilityConfig.projectAbilityPath = [];
1034  abilityConfig.testRunnerFile = [];
1035}
1036
1037function resetProjectConfig() {
1038  projectConfig.entryObj = {};
1039  projectConfig.cardObj = {};
1040  projectConfig.compileHar = false;
1041  projectConfig.compileShared = false;
1042  projectConfig.packageDir = 'node_modules';
1043  projectConfig.packageJson = 'package.json';
1044  projectConfig.packageManagerType = 'npm';
1045  projectConfig.cardEntryObj = {};
1046  projectConfig.compilerTypes = [];
1047  projectConfig.optLazyForEach = false;
1048  projectConfig.hspResourcesMap = false;
1049  projectConfig.coldReload = undefined;
1050  projectConfig.isFirstBuild = undefined;
1051  projectConfig.changedFileList = undefined;
1052  projectConfig.patchAbcPath = undefined;
1053  const props = ['projectPath', 'buildPath', 'aceModuleBuild', 'manifestFilePath', 'aceProfilePath',
1054    'aceModuleJsonPath', 'aceSuperVisualPath', 'hashProjectPath', 'aceBuildJson', 'cachePath',
1055    'aceSoPath', 'localPropertiesPath', 'projectProfilePath', 'isPreview', 'compileMode', 'runtimeOS',
1056    'sdkInfo', 'checkEntry', 'obfuscateHarType', 'isCrossplatform', 'enableDebugLine', 'bundleType'
1057  ];
1058  for (let key in projectConfig) {
1059    if (props.includes(key)) {
1060      projectConfig[key] = undefined;
1061    }
1062  }
1063}
1064
1065function resetGlobalProgram() {
1066  globalProgram.builderProgram = null;
1067  globalProgram.program = null;
1068  globalProgram.checker = null;
1069  globalProgram.strictLanguageService = null;
1070}
1071
1072function initMain() {
1073  staticPreviewPage = process.env.aceStaticPreview;
1074  aceCompileMode = process.env.aceCompileMode || 'page';
1075  abilityConfig.abilityType = process.env.abilityType || 'page';
1076}
1077
1078exports.globalProgram = globalProgram;
1079exports.projectConfig = projectConfig;
1080exports.loadEntryObj = loadEntryObj;
1081exports.readAppResource = readAppResource;
1082exports.resources = resources;
1083exports.loadWorker = loadWorker;
1084exports.abilityConfig = abilityConfig;
1085exports.readWorkerFile = readWorkerFile;
1086exports.abilityPagesFullPath = abilityPagesFullPath;
1087exports.loadModuleInfo = loadModuleInfo;
1088exports.systemModules = systemModules;
1089exports.checkAppResourcePath = checkAppResourcePath;
1090exports.addSDKBuildDependencies = addSDKBuildDependencies;
1091exports.partialUpdateConfig = partialUpdateConfig;
1092exports.readPatchConfig = readPatchConfig;
1093exports.initBuildInfo = initBuildInfo;
1094exports.getCleanConfig = getCleanConfig;
1095exports.globalModulePaths = globalModulePaths;
1096exports.defaultSdkConfigs = defaultSdkConfigs;
1097exports.extendSdkConfigs = extendSdkConfigs;
1098exports.sdkConfigs = sdkConfigs;
1099exports.sdkConfigPrefix = sdkConfigPrefix;
1100exports.ohosSystemModulePaths = ohosSystemModulePaths;
1101exports.resetMain = resetMain;
1102exports.ohosSystemModuleSubDirPaths = ohosSystemModuleSubDirPaths;
1103exports.allModulesPaths = allModulesPaths;
1104exports.resetProjectConfig = resetProjectConfig;
1105exports.resetGlobalProgram = resetGlobalProgram;
1106