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