• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 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
16import fs from 'fs';
17import path from 'path';
18import * as ts from 'typescript';
19import * as crypto from 'crypto';
20const fse = require('fs-extra');
21
22import {
23  projectConfig,
24  systemModules,
25  globalProgram,
26  sdkConfigs,
27  sdkConfigPrefix,
28  allModulesPaths,
29  partialUpdateConfig,
30  resetProjectConfig,
31  resetGlobalProgram
32} from '../main';
33import {
34  preprocessExtend,
35  preprocessNewExtend
36} from './validate_ui_syntax';
37import {
38  INNER_COMPONENT_MEMBER_DECORATORS,
39  COMPONENT_DECORATORS_PARAMS,
40  COMPONENT_BUILD_FUNCTION,
41  STYLE_ADD_DOUBLE_DOLLAR,
42  $$,
43  PROPERTIES_ADD_DOUBLE_DOLLAR,
44  $$_BLOCK_INTERFACE,
45  COMPONENT_EXTEND_DECORATOR,
46  COMPONENT_BUILDER_DECORATOR,
47  ESMODULE,
48  EXTNAME_D_ETS,
49  EXTNAME_JS,
50  FOREACH_LAZYFOREACH,
51  COMPONENT_IF,
52  TS_WATCH_END_MSG,
53  FORM_TAG_CHECK_NAME,
54  FORM_TAG_CHECK_ERROR,
55  CROSSPLATFORM_TAG_CHECK_NAME,
56  CROSSPLATFORM_TAG_CHECK_ERROER,
57  DEPRECATED_TAG_CHECK_NAME,
58  DEPRECATED_TAG_CHECK_WARNING,
59  FA_TAG_CHECK_NAME,
60  FA_TAG_HUMP_CHECK_NAME,
61  FA_TAG_CHECK_ERROR,
62  STAGE_TAG_CHECK_NAME,
63  STAGE_TAG_HUMP_CHECK_NAME,
64  STAGE_TAG_CHECK_ERROR,
65  STAGE_COMPILE_MODE,
66  ATOMICSERVICE_BUNDLE_TYPE,
67  ATOMICSERVICE_TAG_CHECK_NAME,
68  ATOMICSERVICE_TAG_CHECK_ERROER,
69  SINCE_TAG_NAME,
70  ATOMICSERVICE_TAG_CHECK_VERSION,
71  TS_BUILD_INFO_SUFFIX,
72  HOT_RELOAD_BUILD_INFO_SUFFIX,
73  FIND_MODULE_WARNING,
74  SYSCAP_TAG_CHECK_NAME,
75  SYSCAP_TAG_CHECK_WARNING,
76} from './pre_define';
77import { getName } from './process_component_build';
78import {
79  INNER_COMPONENT_NAMES,
80  JS_BIND_COMPONENTS
81} from './component_map';
82import { logger } from './compile_info';
83import {
84  hasDecorator,
85  isString,
86  generateSourceFilesInHar,
87  startTimeStatisticsLocation,
88  stopTimeStatisticsLocation,
89  resolveModuleNamesTime,
90  CompilationTimeStatistics,
91  storedFileInfo,
92  getRollupCacheStoreKey,
93  getRollupCacheKey,
94  clearRollupCacheStore,
95  toUnixPath,
96  isWindows,
97  isMac,
98  tryToLowerCasePath
99} from './utils';
100import { isExtendFunction, isOriginalExtend } from './process_ui_syntax';
101import { visualTransform } from './process_visual';
102import { tsWatchEmitter } from './fast_build/ets_ui/rollup-plugin-ets-checker';
103import {
104  doArkTSLinter,
105  ArkTSLinterMode,
106  ArkTSProgram,
107  ArkTSVersion,
108  getReverseStrictBuilderProgram,
109  wasOptionsStrict
110} from './do_arkTS_linter';
111
112export interface LanguageServiceCache {
113  service?: ts.LanguageService;
114  pkgJsonFileHash?: string;
115}
116
117export const SOURCE_FILES: Map<string, ts.SourceFile> = new Map();
118
119function collectSourceFilesMap(program: ts.Program): void {
120  program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => {
121    SOURCE_FILES.set(path.normalize(sourceFile.fileName), sourceFile);
122  });
123}
124
125export function readDeaclareFiles(): string[] {
126  const declarationsFileNames: string[] = [];
127  fs.readdirSync(path.resolve(__dirname, '../declarations'))
128    .forEach((fileName: string) => {
129      if (/\.d\.ts$/.test(fileName)) {
130        declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName));
131      }
132    });
133  return declarationsFileNames;
134}
135
136const buildInfoWriteFile: ts.WriteFileCallback = (fileName: string, data: string) => {
137  if (fileName.endsWith(TS_BUILD_INFO_SUFFIX)) {
138    let fd: number = fs.openSync(fileName, 'w');
139    fs.writeSync(fd, data, undefined, 'utf8');
140    fs.closeSync(fd);
141  };
142}
143// The collection records the file name and the corresponding version, where the version is the hash value of the text in last compilation.
144const filesBuildInfo: Map<string, string> = new Map();
145
146export const compilerOptions: ts.CompilerOptions = ts.readConfigFile(
147  path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
148function setCompilerOptions(resolveModulePaths: string[]): void {
149  const allPath: Array<string> = ['*'];
150  const basePath: string = path.resolve(projectConfig.projectPath);
151  if (process.env.compileTool === 'rollup' && resolveModulePaths && resolveModulePaths.length) {
152    resolveModulePaths.forEach((item: string) => {
153      if (!(/oh_modules$/.test(item) || /node_modules$/.test(item))) {
154        allPath.push(path.join(path.relative(basePath, item), '*'));
155      }
156    });
157  } else {
158    if (!projectConfig.aceModuleJsonPath) {
159      allPath.push('../../../../../*');
160      allPath.push('../../*');
161    } else {
162      allPath.push('../../../../*');
163      allPath.push('../*');
164    }
165  }
166  const suffix: string = projectConfig.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : TS_BUILD_INFO_SUFFIX;
167  const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', suffix);
168  Object.assign(compilerOptions, {
169    'allowJs': false,
170    'emitNodeModulesFiles': true,
171    'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve,
172    'module': ts.ModuleKind.CommonJS,
173    'moduleResolution': ts.ModuleResolutionKind.NodeJs,
174    'noEmit': true,
175    'target': ts.ScriptTarget.ES2017,
176    'baseUrl': basePath,
177    'paths': {
178      '*': allPath
179    },
180    'lib': ['lib.es2020.d.ts'],
181    'types': projectConfig.compilerTypes,
182    'etsLoaderPath': projectConfig.etsLoaderPath,
183    'needDoArkTsLinter': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE,
184    'isCompatibleVersion': getArkTSLinterMode() === ArkTSLinterMode.COMPATIBLE_MODE,
185    'skipTscOhModuleCheck': partialUpdateConfig.skipTscOhModuleCheck,
186    'skipArkTSStaticBlocksCheck': partialUpdateConfig.skipArkTSStaticBlocksCheck,
187    // options incremental && tsBuildInfoFile are required for applying incremental ability of typescript
188    'incremental': true,
189    'tsBuildInfoFile': buildInfoPath
190  });
191  if (projectConfig.compileMode === ESMODULE) {
192    Object.assign(compilerOptions, {
193      'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Remove,
194      'module': ts.ModuleKind.ES2020
195    });
196  }
197  if (projectConfig.packageDir === 'oh_modules') {
198    Object.assign(compilerOptions, { 'packageManagerType': 'ohpm' });
199  }
200  readTsBuildInfoFileInCrementalMode(buildInfoPath, projectConfig);
201}
202
203/**
204 * Read the source code information in the project of the last compilation process, and then use it
205 * to determine whether the file has been modified during this compilation process.
206 */
207function readTsBuildInfoFileInCrementalMode(buildInfoPath: string, projectConfig: Object): void {
208  if (!fs.existsSync(buildInfoPath) || !(projectConfig.compileHar || projectConfig.compileShared)) {
209    return;
210  }
211
212  type FileInfoType = {
213    version: string;
214    affectsGlobalScope: boolean;
215  }
216  type ProgramType = {
217    fileNames: string[];
218    fileInfos: (FileInfoType | string)[];
219  }
220  let buildInfoProgram: ProgramType = undefined;
221  try {
222    const content: { program: ProgramType } = JSON.parse(fs.readFileSync(buildInfoPath, 'utf-8'));
223    buildInfoProgram = content.program;
224    if (!buildInfoProgram || !buildInfoProgram.fileNames || !buildInfoProgram.fileInfos) {
225      throw new Error('.tsbuildinfo content is invalid');
226    }
227  } catch (err) {
228    fastBuildLogger.warn('\u001b[33m' + 'ArkTS: Failed to parse .tsbuildinfo file. Error message: ' + err.message.toString());
229    return;
230  }
231  const buildInfoDirectory: string = path.dirname(buildInfoPath);
232  /**
233   * For the windos and mac platform, the file path in tsbuildinfo is in lowercase, while buildInfoDirectory is the original path (including uppercase).
234   * Therefore, the path needs to be converted to lowercase, and then perform path comparison.
235   */
236  const isMacOrWin = isWindows() || isMac();
237  const fileNames: string[] = buildInfoProgram.fileNames;
238  const fileInfos: (FileInfoType | string)[] = buildInfoProgram.fileInfos;
239  fileInfos.forEach((fileInfo, index) => {
240    const version: string = typeof fileInfo === 'string' ? fileInfo : fileInfo.version;
241    const absPath: string = path.resolve(buildInfoDirectory, fileNames[index]);
242    filesBuildInfo.set(isMacOrWin ? tryToLowerCasePath(absPath) : absPath, version);
243  });
244}
245
246function getJsDocNodeCheckConfigItem(tagName: string[], message: string, type: ts.DiagnosticCategory,
247  tagNameShouldExisted: boolean, checkValidCallback?: (jsDocTag: ts.JSDocTag, config: ts.JsDocNodeCheckConfigItem) => boolean,
248  checkJsDocSpecialValidCallback?: (jsDocTags: readonly ts.JSDocTag[], config: ts.JsDocNodeCheckConfigItem) => boolean): ts.JsDocNodeCheckConfigItem {
249  return {
250    tagName: tagName,
251    message: message,
252    needConditionCheck: false,
253    type: type,
254    specifyCheckConditionFuncName: '',
255    tagNameShouldExisted: tagNameShouldExisted,
256    checkValidCallback: checkValidCallback,
257    checkJsDocSpecialValidCallback: checkJsDocSpecialValidCallback
258  };
259}
260
261function checkSyscapAbility(jsDocTags: readonly ts.JSDocTag[], config: ts.JsDocNodeCheckConfigItem): boolean {
262  let currentSyscapValue: string = '';
263  for (let i = 0; i < jsDocTags.length; i++) {
264    const jsDocTag: ts.JSDocTag = jsDocTags[i];
265    if (jsDocTag.tagName.escapedText.toString() === SYSCAP_TAG_CHECK_NAME) {
266      currentSyscapValue = jsDocTag.comment as string;
267      break;
268    }
269  }
270  return projectConfig.syscapIntersectionSet && !projectConfig.syscapIntersectionSet.has(currentSyscapValue);
271}
272
273function getJsDocNodeCheckConfig(fileName: string, sourceFileName: string): ts.JsDocNodeCheckConfig {
274  let needCheckResult: boolean = false;
275  const checkConfigArray: ts.JsDocNodeCheckConfigItem[] = [];
276  const apiName: string = path.basename(fileName);
277  const sourceBaseName: string = path.basename(sourceFileName);
278  if (/(?<!\.d)\.ts$/g.test(fileName) && isArkuiDependence(sourceFileName) &&
279    sourceBaseName !== 'common_ts_ets_api.d.ts' && sourceBaseName !== 'global.d.ts') {
280    checkConfigArray.push(getJsDocNodeCheckConfigItem([], FIND_MODULE_WARNING, ts.DiagnosticCategory.Warning, true));
281  }
282  if (!systemModules.includes(apiName) && (allModulesPaths.includes(path.normalize(sourceFileName)) || isArkuiDependence(sourceFileName))) {
283    checkConfigArray.push(getJsDocNodeCheckConfigItem([DEPRECATED_TAG_CHECK_NAME], DEPRECATED_TAG_CHECK_WARNING, ts.DiagnosticCategory.Warning, false));
284    checkConfigArray.push(getJsDocNodeCheckConfigItem([SYSCAP_TAG_CHECK_NAME], SYSCAP_TAG_CHECK_WARNING.replace('$DT', projectConfig.deviceTypesMessage), ts.DiagnosticCategory.Warning, false, null, checkSyscapAbility));
285    if (isCardFile(fileName)) {
286      needCheckResult = true;
287      checkConfigArray.push(getJsDocNodeCheckConfigItem([FORM_TAG_CHECK_NAME], FORM_TAG_CHECK_ERROR, ts.DiagnosticCategory.Error, true));
288    }
289    if (projectConfig.isCrossplatform) {
290      needCheckResult = true;
291      checkConfigArray.push(getJsDocNodeCheckConfigItem([CROSSPLATFORM_TAG_CHECK_NAME], CROSSPLATFORM_TAG_CHECK_ERROER, ts.DiagnosticCategory.Error, true));
292    }
293    if (process.env.compileMode === STAGE_COMPILE_MODE) {
294      needCheckResult = true;
295      checkConfigArray.push(getJsDocNodeCheckConfigItem([FA_TAG_CHECK_NAME, FA_TAG_HUMP_CHECK_NAME], FA_TAG_CHECK_ERROR, ts.DiagnosticCategory.Warning, false));
296    } else if (process.env.compileMode !== '') {
297      needCheckResult = true;
298      checkConfigArray.push(getJsDocNodeCheckConfigItem([STAGE_TAG_CHECK_NAME, STAGE_TAG_HUMP_CHECK_NAME], STAGE_TAG_CHECK_ERROR,
299        ts.DiagnosticCategory.Warning, false));
300    }
301    if (projectConfig.bundleType === ATOMICSERVICE_BUNDLE_TYPE && projectConfig.compileSdkVersion >= ATOMICSERVICE_TAG_CHECK_VERSION) {
302      needCheckResult = true;
303      checkConfigArray.push(getJsDocNodeCheckConfigItem([ATOMICSERVICE_TAG_CHECK_NAME], ATOMICSERVICE_TAG_CHECK_ERROER,
304        ts.DiagnosticCategory.Error, true));
305    }
306  }
307
308  return {
309    nodeNeedCheck: needCheckResult,
310    checkConfig: checkConfigArray
311  };
312}
313
314interface extendInfo {
315  start: number,
316  end: number,
317  compName: string
318}
319
320function createHash(str: string): string {
321  const hash = crypto.createHash('sha256');
322  hash.update(str);
323  return hash.digest('hex');
324}
325
326export const fileHashScriptVersion: (fileName: string) => string = (fileName: string) => {
327  if (!fs.existsSync(fileName)) {
328    return '0';
329  }
330  return createHash(fs.readFileSync(fileName).toString());
331}
332
333export function createLanguageService(rootFileNames: string[], resolveModulePaths: string[],
334  compilationTime: CompilationTimeStatistics = null, rollupShareObject?: any): ts.LanguageService {
335  setCompilerOptions(resolveModulePaths);
336  const servicesHost: ts.LanguageServiceHost = {
337    getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()],
338    getScriptVersion: fileHashScriptVersion,
339    getScriptSnapshot: function(fileName) {
340      if (!fs.existsSync(fileName)) {
341        return undefined;
342      }
343      if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
344        startTimeStatisticsLocation(compilationTime ? compilationTime.scriptSnapshotTime : undefined);
345        appComponentCollection.set(path.join(fileName), new Set());
346        let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
347        const extendFunctionInfo: extendInfo[] = [];
348        content = instanceInsteadThis(content, fileName, extendFunctionInfo, this.uiProps);
349        stopTimeStatisticsLocation(compilationTime ? compilationTime.scriptSnapshotTime : undefined);
350        return ts.ScriptSnapshot.fromString(content);
351      }
352      return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString());
353    },
354    getCurrentDirectory: () => process.cwd(),
355    getCompilationSettings: () => compilerOptions,
356    getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
357    fileExists: ts.sys.fileExists,
358    readFile: ts.sys.readFile,
359    readDirectory: ts.sys.readDirectory,
360    resolveModuleNames: resolveModuleNames,
361    resolveTypeReferenceDirectives: resolveTypeReferenceDirectives,
362    directoryExists: ts.sys.directoryExists,
363    getDirectories: ts.sys.getDirectories,
364    getJsDocNodeCheckedConfig: (fileCheckedInfo: ts.FileCheckModuleInfo, sourceFileName: string) => {
365      return getJsDocNodeCheckConfig(fileCheckedInfo.currentFileName, sourceFileName);
366    },
367    getFileCheckedModuleInfo: (containFilePath: string) => {
368      return {
369        fileNeedCheck: true,
370        checkPayload: undefined,
371        currentFileName: containFilePath,
372      };
373    },
374    uiProps: [],
375    clearProps: function() {
376      dollarCollection.clear();
377      decoratorParamsCollection.clear();
378      extendCollection.clear();
379      this.uiProps.length = 0;
380    }
381  };
382
383  if (process.env.watchMode === 'true') {
384    return ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
385  }
386
387  return getOrCreateLanguageService(servicesHost, rootFileNames, rollupShareObject);
388}
389
390function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFileNames: string[],
391  rollupShareObject?: any): ts.LanguageService {
392  let cacheStoreKey: string = getRollupCacheStoreKey(projectConfig);
393  let cacheServiceKey: string = getRollupCacheKey(projectConfig) + '#' + 'service';
394  clearRollupCacheStore(rollupShareObject?.cacheStoreManager, cacheStoreKey);
395
396  let cache: LanguageServiceCache | undefined =
397    rollupShareObject?.cacheStoreManager?.mount(cacheStoreKey).getCache(cacheServiceKey);
398  let service: ts.LanguageService | undefined = cache?.service;
399  const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash;
400  const lastHash: string | undefined= cache?.pkgJsonFileHash;
401  const shouldRebuild: boolean | undefined = currentHash && lastHash && currentHash !== lastHash;
402  if (!service || shouldRebuild) {
403    service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
404  } else {
405    // Found language service from cache, update root files
406    let updateRootFileNames = [...rootFileNames, ...readDeaclareFiles()];
407    service.updateRootFiles(updateRootFileNames);
408  }
409
410  const newCache: LanguageServiceCache = {service: service, pkgJsonFileHash: currentHash};
411  rollupShareObject?.cacheStoreManager?.mount(cacheStoreKey).setCache(cacheServiceKey, newCache);
412  return service;
413}
414
415interface CacheFileName {
416  mtimeMs: number,
417  children: string[],
418  parent: string[],
419  error: boolean
420}
421interface NeedUpdateFlag {
422  flag: boolean;
423}
424interface CheckerResult {
425  count: number
426}
427
428interface WarnCheckerResult {
429  count: number
430}
431
432interface WholeCache {
433  runtimeOS: string,
434  sdkInfo: string,
435  fileList: Cache
436}
437type Cache = Record<string, CacheFileName>;
438export let cache: Cache = {};
439export const hotReloadSupportFiles: Set<string> = new Set();
440export const shouldResolvedFiles: Set<string> = new Set();
441export const appComponentCollection: Map<string, Set<string>> = new Map();
442const allResolvedModules: Set<string> = new Set();
443// all files of tsc and rollup for obfuscation scanning.
444export const allSourceFilePaths: Set<string> = new Set();
445export let props: string[] = [];
446
447export let fastBuildLogger = null;
448
449export const checkerResult: CheckerResult = { count: 0 };
450export const warnCheckerResult: WarnCheckerResult = { count: 0 };
451export let languageService: ts.LanguageService = null;
452export function serviceChecker(rootFileNames: string[], newLogger: Object = null, resolveModulePaths: string[] = null,
453  compilationTime: CompilationTimeStatistics = null, rollupShareObject?: any): void {
454  fastBuildLogger = newLogger;
455  let cacheFile: string = null;
456  if (projectConfig.xtsMode || process.env.watchMode === 'true') {
457    if (projectConfig.hotReload) {
458      rootFileNames.forEach(fileName => {
459        hotReloadSupportFiles.add(fileName);
460      });
461    }
462    languageService = createLanguageService(rootFileNames, resolveModulePaths, compilationTime);
463    props = languageService.getProps();
464  } else {
465    cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache');
466    const wholeCache: WholeCache = fs.existsSync(cacheFile) ?
467      JSON.parse(fs.readFileSync(cacheFile).toString()) :
468      { 'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {} };
469    if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) {
470      cache = wholeCache.fileList;
471    } else {
472      cache = {};
473    }
474    languageService = createLanguageService(rootFileNames, resolveModulePaths, compilationTime, rollupShareObject);
475  }
476  startTimeStatisticsLocation(compilationTime ? compilationTime.createProgramTime : undefined);
477  globalProgram.builderProgram = languageService.getBuilderProgram();
478  globalProgram.program = globalProgram.builderProgram.getProgram();
479  props = languageService.getProps();
480  stopTimeStatisticsLocation(compilationTime ? compilationTime.createProgramTime : undefined);
481
482  collectAllFiles(globalProgram.program);
483  startTimeStatisticsLocation(compilationTime ? compilationTime.runArkTSLinterTime : undefined);
484  runArkTSLinter(rollupShareObject);
485  stopTimeStatisticsLocation(compilationTime ? compilationTime.runArkTSLinterTime : undefined);
486
487  if (process.env.watchMode !== 'true') {
488    processBuildHap(cacheFile, rootFileNames, compilationTime);
489  }
490}
491// collect the compiled files of tsc and rollup for obfuscation scanning.
492export function collectAllFiles(program?: ts.Program, rollupFileList?: IterableIterator<string>): void {
493  if (program) {
494    collectTscFiles(program);
495    return;
496  }
497  mergeRollUpFiles(rollupFileList);
498}
499
500export function collectTscFiles(program: ts.Program): void {
501  const programAllFiles: readonly ts.SourceFile[] = program.getSourceFiles();
502  let projectRootPath: string = projectConfig.projectRootPath;
503  if (!projectRootPath) {
504    return;
505  }
506  projectRootPath = toUnixPath(projectRootPath);
507  const isMacOrWin = isWindows() || isMac();
508  programAllFiles.forEach(sourceFile => {
509    const fileName = toUnixPath(sourceFile.fileName);
510    if (!fileName.startsWith(projectRootPath)) {
511      return;
512    }
513    allSourceFilePaths.add(fileName);
514    // For the windos and mac platform, the file path in filesBuildInfo is in lowercase, while fileName of sourceFile is the original path (including uppercase).
515    if (filesBuildInfo.size > 0 &&
516      Reflect.get(sourceFile, 'version') !== filesBuildInfo.get(isMacOrWin ? tryToLowerCasePath(fileName) : fileName)) {
517      allResolvedModules.add(fileName);
518    }
519  });
520}
521
522export function mergeRollUpFiles(rollupFileList: IterableIterator<string>) {
523  for (const moduleId of rollupFileList) {
524    if (fs.existsSync(moduleId)) {
525      allSourceFilePaths.add(toUnixPath(moduleId));
526    }
527  }
528}
529
530export function emitBuildInfo(): void {
531  globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile);
532}
533
534function processBuildHap(cacheFile: string, rootFileNames: string[], compilationTime: CompilationTimeStatistics): void {
535  startTimeStatisticsLocation(compilationTime ? compilationTime.diagnosticTime : undefined);
536  const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram
537    .getSyntacticDiagnostics()
538    .concat(globalProgram.builderProgram.getSemanticDiagnostics());
539  stopTimeStatisticsLocation(compilationTime ? compilationTime.diagnosticTime : undefined);
540  emitBuildInfo();
541  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
542    printDiagnostic(diagnostic);
543  });
544  if (!projectConfig.xtsMode) {
545    fse.ensureDirSync(projectConfig.cachePath);
546    fs.writeFileSync(cacheFile, JSON.stringify({
547      'runtimeOS': projectConfig.runtimeOS,
548      'sdkInfo': projectConfig.sdkInfo,
549      'fileList': cache
550    }, null, 2));
551  }
552  if (projectConfig.compileHar || projectConfig.compileShared) {
553    let emit: string | undefined = undefined;
554    let writeFile = (fileName: string, text: string, writeByteOrderMark: boolean) => {
555      emit = text;
556    }
557    [...allResolvedModules, ...rootFileNames].forEach(moduleFile => {
558      if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) {
559        try {
560          if ((/\.d\.e?ts$/).test(moduleFile)) {
561            generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile),
562              projectConfig);
563          } else {
564            emit = undefined;
565            let sourcefile = globalProgram.program.getSourceFile(moduleFile);
566            if (sourcefile) {
567              globalProgram.program.emit(sourcefile, writeFile, undefined, true, undefined, true);
568            }
569            if (emit) {
570              generateSourceFilesInHar(moduleFile, emit, '.d' + path.extname(moduleFile), projectConfig);
571            }
572          }
573        } catch (err) { }
574      }
575    });
576    printDeclarationDiagnostics();
577  }
578}
579
580function printDeclarationDiagnostics(): void {
581  globalProgram.builderProgram.getDeclarationDiagnostics().forEach((diagnostic: ts.Diagnostic) => {
582    printDiagnostic(diagnostic);
583  });
584}
585
586export function isArkuiDependence(file: string): boolean {
587  const fileDir: string = path.dirname(file);
588  const declarationsPath: string = path.resolve(__dirname, '../declarations').replace(/\\/g, '/');
589  const componentPath: string = path.resolve(__dirname, '../../../component').replace(/\\/g, '/');
590  if (fileDir === declarationsPath || fileDir === componentPath) {
591    return true;
592  }
593  return false;
594}
595
596function isCardFile(file: string): boolean {
597  for (const key in projectConfig.cardEntryObj) {
598    if (path.normalize(projectConfig.cardEntryObj[key]) === path.normalize(file)) {
599      return true;
600    }
601  }
602  return false;
603}
604
605function containFormError(message: string): boolean {
606  if (/can't support form application./.test(message)) {
607    return true;
608  }
609  return false;
610}
611
612export function printDiagnostic(diagnostic: ts.Diagnostic): void {
613  const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
614  if (validateError(message)) {
615    if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
616      updateErrorFileCache(diagnostic);
617    }
618
619    if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) {
620      return;
621    }
622
623    const logPrefix: string = diagnostic.category === ts.DiagnosticCategory.Error ? 'ERROR' : 'WARN';
624    const etsCheckerLogger = fastBuildLogger ? fastBuildLogger : logger;
625    let logMessage: string;
626    if (logPrefix === 'ERROR') {
627      checkerResult.count += 1;
628    } else {
629      warnCheckerResult.count += 1;
630    }
631    if (diagnostic.file) {
632      const { line, character }: ts.LineAndCharacter =
633        diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
634      logMessage = `ArkTS:${logPrefix} File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`;
635    } else {
636      logMessage = `ArkTS:${logPrefix}: ${message}`;
637    }
638
639    if (diagnostic.category === ts.DiagnosticCategory.Error) {
640      etsCheckerLogger.error('\u001b[31m' + logMessage);
641    } else {
642      etsCheckerLogger.warn('\u001b[33m' + logMessage);
643    }
644  }
645}
646
647function validateError(message: string): boolean {
648  const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/;
649  const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/;
650  if (matchMessage(message, props, propInfoReg) ||
651    matchMessage(message, props, stateInfoReg)) {
652    return false;
653  }
654  return true;
655}
656function matchMessage(message: string, nameArr: any, reg: RegExp): boolean {
657  if (reg.test(message)) {
658    const match: string[] = message.match(reg);
659    if (match[1] && nameArr.includes(match[1])) {
660      return true;
661    }
662  }
663  return false;
664}
665
666function updateErrorFileCache(diagnostic: ts.Diagnostic): void {
667  if (diagnostic.file && cache[path.resolve(diagnostic.file.fileName)]) {
668    cache[path.resolve(diagnostic.file.fileName)].error = true;
669  }
670}
671
672function filterInput(rootFileNames: string[]): string[] {
673  return rootFileNames.filter((file: string) => {
674    const needUpdate: NeedUpdateFlag = { flag: false };
675    const alreadyCheckedFiles: Set<string> = new Set();
676    checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles);
677    if (!needUpdate.flag) {
678      storedFileInfo.changeFiles.push(path.resolve(file));
679    }
680    return needUpdate.flag;
681  });
682}
683
684function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void {
685  if (alreadyCheckedFiles.has(file)) {
686    return;
687  } else {
688    alreadyCheckedFiles.add(file);
689  }
690
691  if (needUpdate.flag) {
692    return;
693  }
694
695  const value: CacheFileName = cache[file];
696  const mtimeMs: number = fs.statSync(file).mtimeMs;
697  if (value) {
698    if (value.error || value.mtimeMs !== mtimeMs) {
699      needUpdate.flag = true;
700      return;
701    }
702    for (let i = 0; i < value.children.length; ++i) {
703      if (fs.existsSync(value.children[i])) {
704        checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles);
705      } else {
706        needUpdate.flag = true;
707      }
708    }
709  } else {
710    cache[file] = { mtimeMs, children: [], parent: [], error: false };
711    needUpdate.flag = true;
712  }
713}
714
715const moduleResolutionHost: ts.ModuleResolutionHost = {
716  fileExists(fileName: string): boolean {
717    return ts.sys.fileExists(fileName);
718  },
719  readFile(fileName: string): string | undefined {
720    return ts.sys.readFile(fileName);
721  },
722  realpath(path: string): string {
723    return ts.sys.realpath(path);
724  },
725  trace(s: string): void {
726    console.info(s);
727  }
728}
729
730export function resolveTypeReferenceDirectives(typeDirectiveNames: string[] | ts.FileReference[]): ts.ResolvedTypeReferenceDirective[] {
731  if (typeDirectiveNames.length === 0) {
732    return [];
733  }
734
735  const resolvedTypeReferenceCache: ts.ResolvedTypeReferenceDirective[] = [];
736  const cache: Map<string, ts.ResolvedTypeReferenceDirective> = new Map<string, ts.ResolvedTypeReferenceDirective>();
737  const containingFile: string = path.join(projectConfig.modulePath, "build-profile.json5");
738
739  for (let entry of typeDirectiveNames) {
740    const typeName = isString(entry) ? entry : entry.fileName.toLowerCase();
741    if (!cache.has(typeName)) {
742      const resolvedFile = ts.resolveTypeReferenceDirective(typeName, containingFile, compilerOptions, moduleResolutionHost);
743      if (!resolvedFile || !resolvedFile.resolvedTypeReferenceDirective) {
744        logger.error('\u001b[31m', `ArkTS:Cannot find type definition file for: ${typeName}\n`);
745      }
746      const result: ts.ResolvedTypeReferenceDirective = resolvedFile.resolvedTypeReferenceDirective;
747      cache.set(typeName, result);
748      resolvedTypeReferenceCache.push(result);
749    }
750  }
751  return resolvedTypeReferenceCache;
752}
753
754const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map();
755
756export function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] {
757  startTimeStatisticsLocation(resolveModuleNamesTime);
758  const resolvedModules: ts.ResolvedModuleFull[] = [];
759  if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile))
760    || !(resolvedModulesCache[path.resolve(containingFile)] &&
761      resolvedModulesCache[path.resolve(containingFile)].length === moduleNames.length)) {
762    for (const moduleName of moduleNames) {
763      const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost);
764      if (result.resolvedModule) {
765        if (result.resolvedModule.resolvedFileName &&
766          path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) {
767          const resultDETSPath: string =
768            result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS);
769          if (ts.sys.fileExists(resultDETSPath)) {
770            resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS));
771          } else {
772            resolvedModules.push(result.resolvedModule);
773          }
774        } else {
775          resolvedModules.push(result.resolvedModule);
776        }
777      } else if (new RegExp(`^@(${sdkConfigPrefix})\\.`, 'i').test(moduleName.trim())) {
778        let apiFileExist: boolean = false;
779        for (let i = 0; i < sdkConfigs.length; i++) {
780          const sdkConfig = sdkConfigs[i];
781          const resolveModuleInfo: ResolveModuleInfo = getRealModulePath(sdkConfig.apiPath, moduleName, ['.d.ts', '.d.ets']);
782          const modulePath: string = resolveModuleInfo.modulePath;
783          const isDETS: boolean = resolveModuleInfo.isEts;
784          if (systemModules.includes(moduleName + (isDETS ? '.d.ets' : '.d.ts')) && ts.sys.fileExists(modulePath)) {
785            resolvedModules.push(getResolveModule(modulePath, isDETS ? '.d.ets' : '.d.ts'));
786            apiFileExist = true;
787            break;
788          }
789        }
790        if (!apiFileExist) {
791          resolvedModules.push(null);
792        }
793      } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) {
794        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
795        if (ts.sys.fileExists(modulePath)) {
796          resolvedModules.push(getResolveModule(modulePath, '.ets'));
797        } else {
798          resolvedModules.push(null);
799        }
800      } else if (/\.ts$/.test(moduleName)) {
801        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
802        if (ts.sys.fileExists(modulePath)) {
803          resolvedModules.push(getResolveModule(modulePath, '.ts'));
804        } else {
805          resolvedModules.push(null);
806        }
807      } else {
808        const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts');
809        const systemDETSModulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ets');
810        const kitModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ts');
811        const kitSystemDETSModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ets');
812        const suffix: string = /\.js$/.test(moduleName) ? '' : '.js';
813        const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix);
814        const fileModulePath: string =
815          path.resolve(__dirname, '../node_modules', moduleName + '/index.js');
816        const DETSModulePath: string = path.resolve(path.dirname(containingFile),
817          /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS);
818        if (ts.sys.fileExists(modulePath)) {
819          resolvedModules.push(getResolveModule(modulePath, '.d.ts'));
820        } else if (ts.sys.fileExists(systemDETSModulePath)) {
821          resolvedModules.push(getResolveModule(systemDETSModulePath, '.d.ets'));
822        } else if (ts.sys.fileExists(kitModulePath)) {
823          resolvedModules.push(getResolveModule(kitModulePath, '.d.ts'));
824        } else if (ts.sys.fileExists(kitSystemDETSModulePath)) {
825          resolvedModules.push(getResolveModule(kitSystemDETSModulePath, '.d.ets'));
826        } else if (ts.sys.fileExists(jsModulePath)) {
827          resolvedModules.push(getResolveModule(jsModulePath, '.js'));
828        } else if (ts.sys.fileExists(fileModulePath)) {
829          resolvedModules.push(getResolveModule(fileModulePath, '.js'));
830        } else if (ts.sys.fileExists(DETSModulePath)) {
831          resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets'));
832        } else {
833          const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main');
834          let DETSModulePathFromModule: string;
835          if (srcIndex > 0) {
836            DETSModulePathFromModule = path.resolve(
837              projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS);
838            if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) {
839              resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets'));
840            } else {
841              resolvedModules.push(null);
842            }
843          } else {
844            resolvedModules.push(null);
845          }
846        }
847      }
848      if (projectConfig.hotReload && resolvedModules.length &&
849        resolvedModules[resolvedModules.length - 1]) {
850        hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName));
851      }
852      if (collectShouldPackedFiles(resolvedModules)) {
853        allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName);
854      }
855    }
856    if (!projectConfig.xtsMode) {
857      createOrUpdateCache(resolvedModules, path.resolve(containingFile));
858    }
859    resolvedModulesCache[path.resolve(containingFile)] = resolvedModules;
860    stopTimeStatisticsLocation(resolveModuleNamesTime);
861    return resolvedModules;
862  }
863  stopTimeStatisticsLocation(resolveModuleNamesTime);
864  return resolvedModulesCache[path.resolve(containingFile)];
865}
866
867export function getRealModulePath(apiDirs: string[], moduleName: string, exts: string[]): ResolveModuleInfo {
868  const resolveResult: ResolveModuleInfo = {
869    modulePath: '',
870    isEts: true
871  };
872  for (let i = 0; i < apiDirs.length; i++) {
873    const dir = apiDirs[i];
874    for (let i = 0; i < exts.length; i++) {
875      const ext = exts[i];
876      const moduleDir = path.resolve(dir, moduleName + ext);
877      if (!fs.existsSync(moduleDir)) {
878        continue;
879      }
880      resolveResult.modulePath = moduleDir;
881      if (ext === '.d.ts') {
882        resolveResult.isEts = false;
883      }
884    }
885  }
886  return resolveResult;
887}
888
889export interface ResolveModuleInfo {
890  modulePath: string;
891  isEts: boolean;
892}
893
894function collectShouldPackedFiles(resolvedModules: ts.ResolvedModuleFull[]): boolean | RegExpMatchArray {
895  return (projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] &&
896    resolvedModules[resolvedModules.length - 1].resolvedFileName &&
897    (path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/) ||
898      path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/\.d\.e?ts$/) &&
899      path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(
900        new RegExp('\\' + path.sep + 'src' + '\\' + path.sep + 'main' + '\\' + path.sep)));
901}
902
903function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void {
904  const children: string[] = [];
905  const error: boolean = false;
906  resolvedModules.forEach(moduleObj => {
907    if (moduleObj && moduleObj.resolvedFileName && /(?<!\.d)\.(ets|ts)$/.test(moduleObj.resolvedFileName)) {
908      const file: string = path.resolve(moduleObj.resolvedFileName);
909      const mtimeMs: number = fs.statSync(file).mtimeMs;
910      children.push(file);
911      const value: CacheFileName = cache[file];
912      if (value) {
913        value.mtimeMs = mtimeMs;
914        value.error = error;
915        value.parent = value.parent || [];
916        value.parent.push(path.resolve(containingFile));
917        value.parent = [...new Set(value.parent)];
918      } else {
919        cache[file] = { mtimeMs, children: [], parent: [containingFile], error };
920      }
921    }
922  });
923  cache[path.resolve(containingFile)] = {
924    mtimeMs: fs.statSync(containingFile).mtimeMs, children,
925    parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ?
926      cache[path.resolve(containingFile)].parent : [], error
927  };
928}
929
930export function createWatchCompilerHost(rootFileNames: string[],
931  reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function,
932  isPipe: boolean = false, resolveModulePaths: string[] = null): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> {
933  if (projectConfig.hotReload) {
934    rootFileNames.forEach(fileName => {
935      hotReloadSupportFiles.add(fileName);
936    });
937  }
938  setCompilerOptions(resolveModulePaths);
939  const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
940  const host = ts.createWatchCompilerHost(
941    [...rootFileNames, ...readDeaclareFiles()], compilerOptions,
942    ts.sys, createProgram, reportDiagnostic,
943    (diagnostic: ts.Diagnostic) => {
944      if ([6031, 6032].includes(diagnostic.code)) {
945        if (!isPipe) {
946          process.env.watchTs = 'start';
947          resetErrorCount();
948        }
949      }
950      // End of compilation in watch mode flag.
951      if ([6193, 6194].includes(diagnostic.code)) {
952        if (!isPipe) {
953          process.env.watchTs = 'end';
954          if (fastBuildLogger) {
955            fastBuildLogger.debug(TS_WATCH_END_MSG);
956            tsWatchEmitter.emit(TS_WATCH_END_MSG);
957          }
958        }
959        delayPrintLogCount();
960      }
961    });
962  host.readFile = (fileName: string) => {
963    if (!fs.existsSync(fileName)) {
964      return undefined;
965    }
966    if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
967      let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
968      const extendFunctionInfo: extendInfo[] = [];
969      content = instanceInsteadThis(content, fileName, extendFunctionInfo, props);
970      return content;
971    }
972    return fs.readFileSync(fileName).toString();
973  };
974  host.resolveModuleNames = resolveModuleNames;
975  host.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives;
976  return host;
977}
978
979export function watchChecker(rootFileNames: string[], newLogger: any = null, resolveModulePaths: string[] = null): void {
980  fastBuildLogger = newLogger;
981  globalProgram.watchProgram = ts.createWatchProgram(
982    createWatchCompilerHost(rootFileNames, printDiagnostic, () => { }, () => { }, false, resolveModulePaths));
983}
984
985export function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[],
986  props: string[]): string {
987  checkUISyntax(content, fileName, extendFunctionInfo, props);
988  extendFunctionInfo.reverse().forEach((item) => {
989    const subStr: string = content.substring(item.start, item.end);
990    const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => {
991      return item1 + item.compName + 'Instance' + item2;
992    });
993    content = content.slice(0, item.start) + insert + content.slice(item.end);
994  });
995  return content;
996}
997
998function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull {
999  return {
1000    resolvedFileName: modulePath,
1001    isExternalLibraryImport: false,
1002    extension: type
1003  };
1004}
1005
1006export const dollarCollection: Set<string> = new Set();
1007export const decoratorParamsCollection: Set<string> = new Set();
1008export const extendCollection: Set<string> = new Set();
1009export const importModuleCollection: Set<string> = new Set();
1010
1011function checkUISyntax(source: string, fileName: string, extendFunctionInfo: extendInfo[], props: string[]): void {
1012  if (/\.ets$/.test(fileName)) {
1013    if (process.env.compileMode === 'moduleJson' ||
1014      path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) {
1015      const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source,
1016        ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
1017      collectComponents(sourceFile);
1018      parseAllNode(sourceFile, sourceFile, extendFunctionInfo);
1019      props.push(...dollarCollection, ...decoratorParamsCollection, ...extendCollection);
1020    }
1021  }
1022}
1023
1024function collectComponents(node: ts.SourceFile): void {
1025  // @ts-ignore
1026  if (process.env.watchMode !== 'true' && node.identifiers && node.identifiers.size) {
1027    // @ts-ignore
1028    for (const key of node.identifiers.keys()) {
1029      if (JS_BIND_COMPONENTS.has(key)) {
1030        appComponentCollection.get(path.join(node.fileName)).add(key);
1031      }
1032    }
1033  }
1034}
1035
1036function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void {
1037  if (ts.isStructDeclaration(node)) {
1038    if (node.members) {
1039      node.members.forEach(item => {
1040        if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
1041          const propertyName: string = item.name.getText();
1042          const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
1043          if (decorators && decorators.length) {
1044            for (let i = 0; i < decorators.length; i++) {
1045              const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
1046              if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1047                dollarCollection.add('$' + propertyName);
1048              }
1049              if (isDecoratorCollection(decorators[i], decoratorName)) {
1050                decoratorParamsCollection.add(decorators[i].expression.arguments[0].getText());
1051              }
1052            }
1053          }
1054        }
1055      });
1056    }
1057  }
1058  if (process.env.watchMode !== 'true' && ts.isIfStatement(node)) {
1059    appComponentCollection.get(path.join(sourceFileNode.fileName)).add(COMPONENT_IF);
1060  }
1061  if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION ||
1062    (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) &&
1063    hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
1064    if (node.body && node.body.statements && node.body.statements.length) {
1065      const checkProp: ts.NodeArray<ts.Statement> = node.body.statements;
1066      checkProp.forEach((item, index) => {
1067        traverseBuild(item, index);
1068      });
1069    }
1070  }
1071  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) {
1072    if (node.body && node.body.statements && node.body.statements.length &&
1073      !isOriginalExtend(node.body)) {
1074      extendFunctionInfo.push({
1075        start: node.pos,
1076        end: node.end,
1077        compName: isExtendFunction(node, { decoratorName: '', componentName: '' })
1078      });
1079    }
1080  }
1081  node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode, extendFunctionInfo));
1082}
1083
1084function isForeachAndLzayForEach(node: ts.Node): boolean {
1085  return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
1086    FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] &&
1087    ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body);
1088}
1089
1090function traverseBuild(node: ts.Node, index: number): void {
1091  if (ts.isExpressionStatement(node)) {
1092    let parentComponentName: string = getName(node);
1093    if (!INNER_COMPONENT_NAMES.has(parentComponentName) && node.parent && node.parent.statements && index >= 1 &&
1094      node.parent.statements[index - 1].expression && node.parent.statements[index - 1].expression.expression) {
1095      parentComponentName = node.parent.statements[index - 1].expression.expression.escapedText;
1096    }
1097    node = node.expression;
1098    if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
1099      ts.isIdentifier(node.expression) && !$$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
1100      node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1101        traverseBuild(item, indexBlock);
1102      });
1103    } else if (isForeachAndLzayForEach(node)) {
1104      node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1105        traverseBuild(item, indexBlock);
1106      });
1107    } else {
1108      loopNodeFindDoubleDollar(node, parentComponentName);
1109      if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
1110        ts.isIdentifier(node.expression)) {
1111        node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1112          traverseBuild(item, indexBlock);
1113        });
1114      }
1115    }
1116  } else if (ts.isIfStatement(node)) {
1117    if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) {
1118      node.thenStatement.statements.forEach((item, indexIfBlock) => {
1119        traverseBuild(item, indexIfBlock);
1120      });
1121    }
1122    if (node.elseStatement && ts.isBlock(node.elseStatement) && node.elseStatement.statements) {
1123      node.elseStatement.statements.forEach((item, indexElseBlock) => {
1124        traverseBuild(item, indexElseBlock);
1125      });
1126    }
1127  }
1128}
1129
1130function isPropertiesAddDoubleDollar(node: ts.Node): boolean {
1131  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) {
1132    return true;
1133  } else if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
1134    ts.isIdentifier(node.expression) && $$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
1135    return true;
1136  } else {
1137    return false;
1138  }
1139}
1140function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void {
1141  while (node) {
1142    if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
1143      const argument: ts.NodeArray<ts.Node> = node.arguments;
1144      const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name;
1145      if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) {
1146        argument.forEach((item: ts.Node) => {
1147          doubleDollarCollection(item);
1148        });
1149      }
1150    } else if (isPropertiesAddDoubleDollar(node)) {
1151      node.arguments.forEach((item: ts.Node) => {
1152        if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) {
1153          item.properties.forEach((param: ts.Node) => {
1154            if (isObjectPram(param, parentComponentName)) {
1155              doubleDollarCollection(param.initializer);
1156            }
1157          });
1158        }
1159        if (STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()) && ts.isPropertyAccessExpression(item)) {
1160          doubleDollarCollection(item);
1161        }
1162      });
1163    }
1164    node = node.expression;
1165  }
1166}
1167
1168function doubleDollarCollection(item: ts.Node): void {
1169  if (item.getText().startsWith($$)) {
1170    while (item.expression) {
1171      item = item.expression;
1172    }
1173    dollarCollection.add(item.getText());
1174  }
1175}
1176
1177function isObjectPram(param: ts.Node, parentComponentName: string): boolean {
1178  return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) &&
1179    param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
1180    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText());
1181}
1182
1183function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean {
1184  return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
1185    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) ||
1186    STYLE_ADD_DOUBLE_DOLLAR.has(propertyName);
1187}
1188
1189function isDecoratorCollection(item: ts.Decorator, decoratorName: string): boolean {
1190  return COMPONENT_DECORATORS_PARAMS.has(decoratorName) &&
1191    // @ts-ignore
1192    item.expression.arguments && item.expression.arguments.length &&
1193    // @ts-ignore
1194    ts.isIdentifier(item.expression.arguments[0]);
1195}
1196
1197function processContent(source: string, id: string): string {
1198  if (fastBuildLogger) {
1199    source = visualTransform(source, id, fastBuildLogger);
1200  }
1201  source = preprocessExtend(source, extendCollection);
1202  source = preprocessNewExtend(source, extendCollection);
1203  return source;
1204}
1205
1206function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void {
1207  if (shouldResolvedFiles.has(file)) {
1208    return;
1209  }
1210  shouldResolvedFiles.add(file);
1211  if (cache && cache[file] && cache[file].parent) {
1212    cache[file].parent.forEach((item) => {
1213      judgeFileShouldResolved(item, shouldResolvedFiles);
1214    });
1215    cache[file].parent = [];
1216  }
1217  if (cache && cache[file] && cache[file].children) {
1218    cache[file].children.forEach((item) => {
1219      judgeFileShouldResolved(item, shouldResolvedFiles);
1220    });
1221    cache[file].children = [];
1222  }
1223}
1224
1225export function incrementWatchFile(watchModifiedFiles: string[],
1226  watchRemovedFiles: string[]): void {
1227  const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles];
1228  if (changedFiles.length) {
1229    shouldResolvedFiles.clear();
1230  }
1231  changedFiles.forEach((file) => {
1232    judgeFileShouldResolved(file, shouldResolvedFiles);
1233  });
1234}
1235
1236function runArkTSLinter(rollupShareObject?: Object): void {
1237  let wasStrict: boolean = wasOptionsStrict(globalProgram.program.getCompilerOptions());
1238  let originProgram: ArkTSProgram = {
1239    builderProgram: globalProgram.builderProgram,
1240    wasStrict: wasStrict
1241  };
1242  let reverseStrictProgram: ArkTSProgram = {
1243    builderProgram: getReverseStrictBuilderProgram(rollupShareObject, globalProgram.program, wasStrict),
1244    wasStrict: !wasStrict
1245  };
1246  const arkTSLinterDiagnostics = doArkTSLinter(getArkTSVersion(),
1247    getArkTSLinterMode(),
1248    originProgram,
1249    reverseStrictProgram,
1250    printArkTSLinterDiagnostic,
1251    !projectConfig.xtsMode,
1252    buildInfoWriteFile);
1253
1254  if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
1255    arkTSLinterDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
1256      updateErrorFileCache(diagnostic);
1257    });
1258  }
1259}
1260
1261function printArkTSLinterDiagnostic(diagnostic: ts.Diagnostic): void {
1262  if (diagnostic.category === ts.DiagnosticCategory.Error && (isInOhModuleFile(diagnostic) || isInSDK(diagnostic))) {
1263    const originalCategory = diagnostic.category;
1264    diagnostic.category = ts.DiagnosticCategory.Warning;
1265    printDiagnostic(diagnostic);
1266    diagnostic.category = originalCategory;
1267    return;
1268  }
1269  printDiagnostic(diagnostic);
1270}
1271
1272function isInOhModuleFile(diagnostics: ts.Diagnostic): boolean {
1273  return (diagnostics.file !== undefined) &&
1274    ((diagnostics.file.fileName.indexOf('/oh_modules/') !== -1) || diagnostics.file.fileName.indexOf('\\oh_modules\\') !== -1);
1275}
1276
1277function isInSDK(diagnostics: ts.Diagnostic): boolean {
1278  const fileName = diagnostics.file?.fileName;
1279  if (projectConfig.etsLoaderPath === undefined || fileName === undefined) {
1280    return false;
1281  }
1282  const sdkPath = path.resolve(projectConfig.etsLoaderPath, '../../../');
1283  return path.resolve(fileName).startsWith(sdkPath);
1284}
1285
1286export function getArkTSLinterMode(): ArkTSLinterMode {
1287  if (!partialUpdateConfig.executeArkTSLinter) {
1288    return ArkTSLinterMode.NOT_USE;
1289  }
1290
1291  if (!partialUpdateConfig.standardArkTSLinter) {
1292    return ArkTSLinterMode.COMPATIBLE_MODE;
1293  }
1294
1295  if (isStandardMode()) {
1296    return ArkTSLinterMode.STANDARD_MODE;
1297  }
1298  return ArkTSLinterMode.COMPATIBLE_MODE;
1299}
1300
1301export function isStandardMode(): boolean {
1302  const STANDARD_MODE_COMPATIBLE_SDK_VERSION = 10;
1303  if (projectConfig &&
1304    projectConfig.compatibleSdkVersion &&
1305    projectConfig.compatibleSdkVersion >= STANDARD_MODE_COMPATIBLE_SDK_VERSION) {
1306    return true;
1307  }
1308  return false;
1309}
1310
1311function getArkTSVersion(): ArkTSVersion {
1312  if (projectConfig.arkTSVersion === '1.0') {
1313    return ArkTSVersion.ArkTS_1_0;
1314  } else if (projectConfig.arkTSVersion === '1.1') {
1315    return ArkTSVersion.ArkTS_1_1;
1316  } else if (projectConfig.arkTSVersion !== undefined) {
1317    const arkTSVersionLogger = fastBuildLogger ? fastBuildLogger : logger;
1318    arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version\n');
1319  }
1320
1321  if (partialUpdateConfig.arkTSVersion === '1.0') {
1322    return ArkTSVersion.ArkTS_1_0;
1323  } else if (partialUpdateConfig.arkTSVersion === '1.1') {
1324    return ArkTSVersion.ArkTS_1_1;
1325  } else if (partialUpdateConfig.arkTSVersion !== undefined) {
1326    const arkTSVersionLogger = fastBuildLogger ? fastBuildLogger : logger;
1327    arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version in metadata\n');
1328  }
1329
1330  return ArkTSVersion.ArkTS_1_1;
1331}
1332
1333function initEtsStandaloneCheckerConfig(logger, config): void {
1334  fastBuildLogger = logger;
1335  if (config.packageManagerType === 'ohpm') {
1336    config.packageDir = 'oh_modules';
1337    config.packageJson = 'oh-package.json5';
1338  } else {
1339    config.packageDir = 'node_modules';
1340    config.packageJson = 'package.json';
1341  }
1342  if (config.aceModuleJsonPath && fs.existsSync(config.aceModuleJsonPath)) {
1343    process.env.compileMode = 'moduleJson';
1344  }
1345  Object.assign(projectConfig, config);
1346}
1347
1348function resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode: string) {
1349  resetProjectConfig();
1350  resetGlobalProgram();
1351  resetEtsCheck();
1352  fastBuildLogger = beforeInitFastBuildLogger;
1353  process.env.compileMode = beforeInitCompileMode;
1354}
1355
1356export function etsStandaloneChecker(entryObj, logger, projectConfig): void {
1357  const beforeInitFastBuildLogger = fastBuildLogger;
1358  const beforeInitCompileMode = process.env.compileMode;
1359  initEtsStandaloneCheckerConfig(logger, projectConfig);
1360  const rootFileNames: string[] = [];
1361  const resolveModulePaths: string[] = [];
1362  Object.values(entryObj).forEach((fileName: string) => {
1363    rootFileNames.push(path.resolve(fileName));
1364  });
1365  if (projectConfig.resolveModulePaths && Array.isArray(projectConfig.resolveModulePaths)) {
1366    resolveModulePaths.push(...projectConfig.resolveModulePaths);
1367  }
1368  const filterFiles: string[] = filterInput(rootFileNames);
1369  languageService = createLanguageService(filterFiles, resolveModulePaths);
1370  globalProgram.builderProgram = languageService.getBuilderProgram();
1371  globalProgram.program = globalProgram.builderProgram.getProgram();
1372  props = languageService.getProps();
1373  runArkTSLinter();
1374  const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram
1375    .getSyntacticDiagnostics()
1376    .concat(globalProgram.builderProgram.getSemanticDiagnostics());
1377  globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile);
1378
1379  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
1380    printDiagnostic(diagnostic);
1381  });
1382  resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode);
1383}
1384
1385export function resetEtsCheck(): void {
1386  cache = {};
1387  props = [];
1388  languageService = null;
1389  allResolvedModules.clear();
1390  checkerResult.count = 0;
1391  warnCheckerResult.count = 0;
1392  resolvedModulesCache.clear();
1393  dollarCollection.clear();
1394  decoratorParamsCollection.clear();
1395  extendCollection.clear();
1396  allSourceFilePaths.clear();
1397  filesBuildInfo.clear();
1398}
1399