• 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  partialUpdateConfig,
29  resetProjectConfig,
30  resetGlobalProgram
31} from '../main';
32import {
33  preprocessExtend,
34  preprocessNewExtend
35} from './validate_ui_syntax';
36import {
37  INNER_COMPONENT_MEMBER_DECORATORS,
38  COMPONENT_DECORATORS_PARAMS,
39  COMPONENT_BUILD_FUNCTION,
40  STYLE_ADD_DOUBLE_DOLLAR,
41  $$,
42  PROPERTIES_ADD_DOUBLE_DOLLAR,
43  DOLLAR_BLOCK_INTERFACE,
44  COMPONENT_EXTEND_DECORATOR,
45  COMPONENT_BUILDER_DECORATOR,
46  ESMODULE,
47  EXTNAME_D_ETS,
48  EXTNAME_JS,
49  EXTNAME_ETS,
50  FOREACH_LAZYFOREACH,
51  COMPONENT_IF,
52  TS_WATCH_END_MSG,
53  TS_BUILD_INFO_SUFFIX,
54  HOT_RELOAD_BUILD_INFO_SUFFIX,
55  WATCH_COMPILER_BUILD_INFO_SUFFIX,
56  COMPONENT_STYLES_DECORATOR
57} from './pre_define';
58import {
59  INNER_COMPONENT_NAMES,
60  JS_BIND_COMPONENTS,
61  BUILDIN_STYLE_NAMES
62} from './component_map';
63import { logger } from './compile_info';
64import {
65  hasDecorator,
66  isString,
67  generateSourceFilesInHar,
68  storedFileInfo,
69  toUnixPath,
70  isWindows,
71  isMac,
72  tryToLowerCasePath,
73  getRollupCache,
74  setRollupCache
75} from './utils';
76import {
77  isExtendFunction,
78  isOriginalExtend
79} from './process_ui_syntax';
80import { visualTransform } from './process_visual';
81import { tsWatchEmitter } from './fast_build/ets_ui/rollup-plugin-ets-checker';
82import {
83  doArkTSLinter,
84  ArkTSLinterMode,
85  ArkTSVersion,
86  transfromErrorCode,
87} from './do_arkTS_linter';
88import {
89  getJsDocNodeCheckConfig,
90  isCardFile,
91  getRealModulePath,
92  getJsDocNodeConditionCheckResult
93} from './fast_build/system_api/api_check_utils';
94import { sourceFileDependencies } from './fast_build/ark_compiler/common/ob_config_resolver';
95import { MemoryMonitor } from './fast_build/meomry_monitor/rollup-plugin-memory-monitor';
96import { MemoryDefine } from './fast_build/meomry_monitor/memory_define';
97import {
98  CompileEvent
99} from './performance';
100import {
101  LINTER_SUBSYSTEM_CODE,
102  HvigorErrorInfo
103} from './hvigor_error_code/hvigor_error_info';
104import { ErrorCodeModule } from './hvigor_error_code/const/error_code_module';
105import { buildErrorInfoFromDiagnostic } from './hvigor_error_code/utils';
106import { concatenateEtsOptions, getExternalComponentPaths } from './external_component_map';
107import {
108  getArkTSEvoDeclFilePath,
109  redirectToDeclFileForInterop
110} from './fast_build/ark_compiler/interop/process_arkts_evolution';
111import {
112  FileManager,
113  getApiPathForInterop,
114  isMixCompile
115} from './fast_build/ark_compiler/interop/interop_manager';
116import {
117  ARKTS_1_1,
118  ARKTS_1_2
119} from './fast_build/ark_compiler/interop/pre_define';
120
121
122export interface LanguageServiceCache {
123  service?: ts.LanguageService;
124  pkgJsonFileHash?: string;
125  targetESVersion?: ts.ScriptTarget;
126  maxFlowDepth?: number;
127  preTsImportSendable?: boolean;
128  preSkipOhModulesLint?: boolean;
129  preMixCompile?: boolean;
130}
131
132export const SOURCE_FILES: Map<string, ts.SourceFile> = new Map();
133export let localPackageSet: Set<string> = new Set();
134export const TSC_SYSTEM_CODE = '105';
135export const fileCache: Map<string, string> = new Map();
136
137export const MAX_FLOW_DEPTH_DEFAULT_VALUE = 2000;
138export const MAX_FLOW_DEPTH_MAXIMUM_VALUE = 65535;
139
140export function readDeaclareFiles(): string[] {
141  const declarationsFileNames: string[] = [];
142  fs.readdirSync(path.resolve(__dirname, '../declarations'))
143    .forEach((fileName: string) => {
144      if (/\.d\.ts$/.test(fileName)) {
145        declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName));
146      }
147    });
148  return declarationsFileNames;
149}
150
151const buildInfoWriteFile: ts.WriteFileCallback = (fileName: string, data: string) => {
152  if (fileName.includes(TS_BUILD_INFO_SUFFIX)) {
153    const fd: number = fs.openSync(fileName, 'w');
154    fs.writeSync(fd, data, undefined, 'utf8');
155    fs.closeSync(fd);
156  }
157};
158// The collection records the file name and the corresponding version, where the version is the hash value of the text in last compilation.
159const filesBuildInfo: Map<string, string> = new Map();
160
161export let compilerOptions = ts.readConfigFile(
162  path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
163const componentPaths: string[] | undefined = getExternalComponentPaths();
164if (componentPaths) {
165  for (const componentPath of componentPaths) {
166    if (!fs.existsSync(componentPath)) {
167      continue;
168    }
169    const externalCompilerOptions: ts.CompilerOptions = ts.readConfigFile(
170      path.resolve(componentPath, 'externalconfig.json'), ts.sys.readFile
171    ).config.compilerOptions;
172    concatenateEtsOptions(compilerOptions, externalCompilerOptions);
173  }
174}
175function setCompilerOptions(resolveModulePaths: string[]): void {
176  const allPath: Array<string> = ['*'];
177  const basePath: string = path.resolve(projectConfig.projectPath);
178  if (process.env.compileTool === 'rollup' && resolveModulePaths && resolveModulePaths.length) {
179    resolveModulePaths.forEach((item: string) => {
180      if (!(/oh_modules$/.test(item) || /node_modules$/.test(item))) {
181        allPath.push(path.join(path.relative(basePath, item), '*'));
182      }
183    });
184  } else {
185    if (!projectConfig.aceModuleJsonPath) {
186      allPath.push('../../../../../*');
187      allPath.push('../../*');
188    } else {
189      allPath.push('../../../../*');
190      allPath.push('../*');
191    }
192  }
193  if (isMixCompile()) {
194    compilerOptions.preserveValueImports = true;
195  }
196  const suffix: string = projectConfig.hotReload ? HOT_RELOAD_BUILD_INFO_SUFFIX : TS_BUILD_INFO_SUFFIX;
197  const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', suffix);
198  checkArkTSVersion();
199  Object.assign(compilerOptions, {
200    'allowJs': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE ? true : false,
201    'checkJs': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE ? false : undefined,
202    'emitNodeModulesFiles': true,
203    'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve,
204    'module': ts.ModuleKind.CommonJS,
205    'moduleResolution': ts.ModuleResolutionKind.NodeJs,
206    'noEmit': true,
207    'target': convertConfigTarget(getTargetESVersion()),
208    'maxFlowDepth': getMaxFlowDepth(),
209    'baseUrl': basePath,
210    'paths': {
211      '*': allPath
212    },
213    'lib': convertConfigLib(getTargetESVersionLib()),
214    'types': projectConfig.compilerTypes,
215    'etsAnnotationsEnable': projectConfig.allowEtsAnnotations,
216    'etsLoaderPath': projectConfig.etsLoaderPath,
217    'needDoArkTsLinter': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE,
218    'isCompatibleVersion': getArkTSLinterMode() === ArkTSLinterMode.COMPATIBLE_MODE,
219    'skipTscOhModuleCheck': partialUpdateConfig.skipTscOhModuleCheck,
220    'skipArkTSStaticBlocksCheck': partialUpdateConfig.skipArkTSStaticBlocksCheck,
221    // options incremental && tsBuildInfoFile are required for applying incremental ability of typescript
222    'incremental': true,
223    'tsBuildInfoFile': buildInfoPath,
224    'tsImportSendableEnable': tsImportSendable,
225    'skipPathsInKeyForCompilationSettings': reuseLanguageServiceForDepChange,
226    'compatibleSdkVersionStage': projectConfig.compatibleSdkVersionStage,
227    'compatibleSdkVersion': projectConfig.compatibleSdkVersion,
228    'skipOhModulesLint': skipOhModulesLint,
229    'mixCompile': mixCompile
230  });
231  if (projectConfig.compileMode === ESMODULE) {
232    Object.assign(compilerOptions, {
233      'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Remove,
234      'module': ts.ModuleKind.ES2020
235    });
236  }
237  if (projectConfig.packageDir === 'oh_modules') {
238    Object.assign(compilerOptions, {'packageManagerType': 'ohpm'});
239  }
240  readTsBuildInfoFileInCrementalMode(buildInfoPath, projectConfig);
241}
242
243function checkArkTSVersion(): void {
244  const etsCheckerLogger = fastBuildLogger || logger;
245  if (getArkTSVersion() === ArkTSVersion.ArkTS_1_0 && tsImportSendable) {
246    const logMessage: string = 'ArkTS: ArkTSVersion1.0 does not support tsImportSendable in any condition';
247    etsCheckerLogger.error('\u001b[31m' + logMessage);
248    tsImportSendable = false;
249  }
250}
251
252// Change target to enum's value,e.g: "es2021" => ts.ScriptTarget.ES2021
253function convertConfigTarget(target: number | string): number | string {
254  if ((typeof target === 'number') && (target in ts.ScriptTarget)) {
255    return target;
256  }
257  return ts.convertCompilerOptionsFromJson({ 'target': target }, '').options.target;
258}
259
260// Change lib to libMap's value,e.g: "es2021" => "lib.es2021.d.ts"
261function convertConfigLib(libs: string[]): string[] {
262  let converted: boolean = true;
263  let libMapValues: string[] = Array.from(ts.libMap.values());
264  for (let i = 0; i < libs.length; i++) {
265    if (!libMapValues.includes(libs[i])) {
266      converted = false;
267      break;
268    }
269  }
270  if (converted) {
271    return libs;
272  }
273  return ts.convertCompilerOptionsFromJson({ 'lib': libs }, '').options.lib;
274}
275
276/**
277 * Read the source code information in the project of the last compilation process, and then use it
278 * to determine whether the file has been modified during this compilation process.
279 */
280function readTsBuildInfoFileInCrementalMode(buildInfoPath: string, projectConfig: Object): void {
281  if (!fs.existsSync(buildInfoPath) || !(projectConfig.compileHar || projectConfig.compileShared)) {
282    return;
283  }
284
285  type FileInfoType = {
286    version: string;
287    affectsGlobalScope: boolean;
288  };
289  type ProgramType = {
290    fileNames: string[];
291    fileInfos: (FileInfoType | string)[];
292  };
293  let buildInfoProgram: ProgramType;
294  try {
295    const content: {program: ProgramType} = JSON.parse(fs.readFileSync(buildInfoPath, 'utf-8'));
296    buildInfoProgram = content.program;
297    if (!buildInfoProgram || !buildInfoProgram.fileNames || !buildInfoProgram.fileInfos) {
298      throw new Error('.tsbuildinfo content is invalid');
299    }
300  } catch (err) {
301    fastBuildLogger.warn('\u001b[33m' + 'ArkTS: Failed to parse .tsbuildinfo file. Error message: ' + err.message.toString());
302    return;
303  }
304  const buildInfoDirectory: string = path.dirname(buildInfoPath);
305  /**
306   * For the windos and mac platform, the file path in tsbuildinfo is in lowercase, while buildInfoDirectory is the original path (including uppercase).
307   * Therefore, the path needs to be converted to lowercase, and then perform path comparison.
308   */
309  const isMacOrWin = isWindows() || isMac();
310  const fileNames: string[] = buildInfoProgram.fileNames;
311  const fileInfos: (FileInfoType | string)[] = buildInfoProgram.fileInfos;
312  fileInfos.forEach((fileInfo, index) => {
313    const version: string = typeof fileInfo === 'string' ? fileInfo : fileInfo.version;
314    const absPath: string = path.resolve(buildInfoDirectory, fileNames[index]);
315    filesBuildInfo.set(isMacOrWin ? tryToLowerCasePath(absPath) : absPath, version);
316  });
317}
318
319interface extendInfo {
320  start: number,
321  end: number,
322  compName: string
323}
324
325function createHash(str: string): string {
326  const hash = crypto.createHash('sha256');
327  hash.update(str);
328  return hash.digest('hex');
329}
330
331export function getFileContentWithHash(fileName: string): string {
332  let fileContent: string | undefined = fileCache.get(fileName);
333  if (fileContent === undefined) {
334    fileContent = fs.readFileSync(fileName).toString();
335    fileCache.set(fileName, fileContent);
336    // Provide the hash value for hvigor's remote cache, and let them handle the cleanup.
337    setHashValueByFilePath?.(fileName, createHash(fileContent));
338  }
339  return fileContent;
340}
341
342export const fileHashScriptVersion: (fileName: string) => string = (fileName: string) => {
343  if (!fs.existsSync(fileName)) {
344    return '0';
345  }
346
347  let fileContent: string = getFileContentWithHash(fileName);
348  let cacheInfo: CacheFileName = cache[path.resolve(fileName)];
349
350  // Error code corresponding to message `Cannot find module xx or its corresponding type declarations`
351  const errorCodeRequireRecheck: number = 2307;
352
353  if (cacheInfo && cacheInfo.error === true && cacheInfo.errorCodes && cacheInfo.errorCodes.includes(errorCodeRequireRecheck)) {
354    // If this file had errors that require recheck in the last compilation,
355    // mark the file as modified by modifying its hash value, thereby triggering tsc to recheck.
356    fileContent += Date.now().toString();
357    return createHash(fileContent);
358  }
359  return getHashByFilePath?.(fileName) ?? createHash(fileContent);
360};
361
362// Reuse the last language service when dependency in oh-package.json5 changes to enhance performance in incremental building.
363// Setting this to false will create a new language service on dependency changes, like a full rebuild.
364const reuseLanguageServiceForDepChange: boolean = true;
365// When dependency changes and reusing the last language service, enable this flag to recheck code dependent on those dependencies.
366export let needReCheckForChangedDepUsers: boolean = false;
367let setHashValueByFilePath: Function | undefined = undefined;
368let getHashByFilePath: Function | undefined = undefined;
369
370export function createLanguageService(rootFileNames: string[], resolveModulePaths: string[],
371  parentEvent?: CompileEvent, rollupShareObject?: Object): ts.LanguageService {
372  setHashValueByFilePath = rollupShareObject?.setHashValueByFilePath;
373  getHashByFilePath = rollupShareObject?.getHashByFilePath;
374  setCompilerOptions(resolveModulePaths);
375  const servicesHost: ts.LanguageServiceHost = {
376    getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()],
377    getScriptVersion: fileHashScriptVersion,
378    getScriptSnapshot: function(fileName) {
379      if (!fs.existsSync(fileName)) {
380        return undefined;
381      }
382      let fileContent: string = getFileContentWithHash(fileName);
383      if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
384        ts.PerformanceDotting.startAdvanced('scriptSnapshot');
385        appComponentCollection.set(path.join(fileName), new Set());
386        let content: string = processContent(fileContent, fileName);
387        const extendFunctionInfo: extendInfo[] = [];
388        content = instanceInsteadThis(content, fileName, extendFunctionInfo, this.uiProps);
389        ts.PerformanceDotting.stopAdvanced('scriptSnapshot');
390        return ts.ScriptSnapshot.fromString(content);
391      }
392      return ts.ScriptSnapshot.fromString(fileContent);
393    },
394    getCurrentDirectory: () => process.cwd(),
395    getCompilationSettings: () => compilerOptions,
396    getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
397    fileExists: ts.sys.fileExists,
398    readFile: ts.sys.readFile,
399    readDirectory: ts.sys.readDirectory,
400    resolveModuleNames: resolveModuleNames,
401    resolveTypeReferenceDirectives: resolveTypeReferenceDirectives,
402    directoryExists: ts.sys.directoryExists,
403    getDirectories: ts.sys.getDirectories,
404    getJsDocNodeCheckedConfig: (fileCheckedInfo: ts.FileCheckModuleInfo, sourceFileName: string) => {
405      return getJsDocNodeCheckConfig(fileCheckedInfo.currentFileName, sourceFileName);
406    },
407    getFileCheckedModuleInfo: (containFilePath: string) => {
408      return {
409        fileNeedCheck: true,
410        checkPayload: undefined,
411        currentFileName: containFilePath
412      };
413    },
414    getJsDocNodeConditionCheckedResult: (jsDocFileCheckedInfo: ts.FileCheckModuleInfo, jsDocInfos: ts.JsDocTagInfo[], jsDocs?: ts.JSDoc[]) => {
415      return getJsDocNodeConditionCheckResult(jsDocFileCheckedInfo, jsDocInfos, jsDocs);
416    },
417    uiProps: new Set(),
418    clearProps: function() {
419      dollarCollection.clear();
420      extendCollection.clear();
421      newExtendCollection.clear();
422      this.uiProps = new Set();
423    },
424    // TSC will re-do resolution if this callback return true.
425    hasInvalidatedResolutions: (filePath: string): boolean => {
426      return reuseLanguageServiceForDepChange && needReCheckForChangedDepUsers;
427    },
428    clearFileCache: function() {
429      fileCache.clear();
430    },
431    isStaticSourceFile: (fileName: string): boolean => {
432      const languageVersion = FileManager.getInstance().getLanguageVersionByFilePath(fileName);
433      return languageVersion?.languageVersion === ARKTS_1_2;
434    },
435  };
436  ts.PerformanceDotting?.setPerformanceSwitch(projectConfig?.perf);
437
438  if (process.env.watchMode === 'true') {
439    const recordInfo = MemoryMonitor.recordStage(MemoryDefine.ETS_CHECKER_CREATE_LANGUAGE_SERVICE);
440    const tsLanguageService = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
441    MemoryMonitor.stopRecordStage(recordInfo);
442    return tsLanguageService;
443  }
444  const recordInfo = MemoryMonitor.recordStage(MemoryDefine.ETS_CHECKER_CREATE_LANGUAGE_SERVICE);
445  const tsLanguageService = getOrCreateLanguageService(servicesHost, rootFileNames, rollupShareObject);
446  MemoryMonitor.stopRecordStage(recordInfo);
447  return tsLanguageService;
448}
449
450export let targetESVersionChanged: boolean = false;
451
452function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFileNames: string[],
453  rollupShareObject?: Object): ts.LanguageService {
454  let cacheKey: string = 'service';
455  let cache: LanguageServiceCache | undefined = getRollupCache(rollupShareObject, projectConfig, cacheKey);
456
457  let service: ts.LanguageService | undefined = cache?.service;
458  const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash;
459  const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target;
460  const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth;
461  const lastHash: string | undefined = cache?.pkgJsonFileHash;
462  const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion;
463  const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth;
464  const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash;
465  const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ?
466    (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers;
467  const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion;
468  const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth;
469  const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ?
470    false :
471    cache?.preTsImportSendable !== tsImportSendable;
472  const skipOhModulesLintDiff: boolean = (cache?.preSkipOhModulesLint === undefined && !skipOhModulesLint) ?
473    false : cache?.preSkipOhModulesLint !== skipOhModulesLint;
474  const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ?
475    false : cache?.preMixCompile !== mixCompile;
476  const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers ||
477    tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff;
478  if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) {
479    needReCheckForChangedDepUsers = true;
480  }
481
482  if (!service || shouldRebuild) {
483    rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers, skipOhModulesLintDiff, mixCompileDiff);
484    service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
485  } else {
486    // Found language service from cache, update root files
487    const updateRootFileNames = [...rootFileNames, ...readDeaclareFiles()];
488    service.updateRootFiles(updateRootFileNames);
489  }
490
491  const newCache: LanguageServiceCache = {
492    service: service,
493    pkgJsonFileHash: currentHash,
494    targetESVersion: currentTargetESVersion,
495    maxFlowDepth: currentMaxFlowDepth,
496    preTsImportSendable: tsImportSendable,
497    preSkipOhModulesLint: skipOhModulesLint,
498    preMixCompile: mixCompile
499  };
500  setRollupCache(rollupShareObject, projectConfig, cacheKey, newCache);
501  return service;
502}
503
504function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean,
505  maxFlowDepthDiffers: boolean | undefined, skipOhModulesLintDiff: boolean, mixCompileDiff: boolean): void {
506  if (targetESVersionDiffers) {
507    // If the targetESVersion is changed, we need to delete the build info cahce files
508    deleteBuildInfoCache(compilerOptions.tsBuildInfoFile);
509    targetESVersionChanged = true;
510  } else if (tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff) {
511    // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files
512    deleteBuildInfoCache(compilerOptions.tsBuildInfoFile);
513  }
514}
515
516function deleteBuildInfoCache(tsBuildInfoFilePath: string): void {
517  // The file name of tsBuildInfoLinterFile is '.tsbuildinfo.linter', so we need to add '.linter' after tsBuildInfoFilePath
518  const tsBuildInfoLinterFilePath: string = tsBuildInfoFilePath + '.linter';
519  deleteFile(tsBuildInfoFilePath);
520  deleteFile(tsBuildInfoLinterFilePath);
521}
522
523function deleteFile(filePath: string): void {
524  if (fs.existsSync(filePath)) {
525      fs.unlinkSync(filePath);
526  }
527}
528
529interface CacheFileName {
530  mtimeMs: number,
531  children: string[],
532  parent: string[],
533  error: boolean,
534  errorCodes?: number[]
535}
536interface NeedUpdateFlag {
537  flag: boolean;
538}
539interface CheckerResult {
540  count: number
541}
542
543interface WarnCheckerResult {
544  count: number
545}
546
547interface WholeCache {
548  runtimeOS: string,
549  sdkInfo: string,
550  fileList: Cache
551}
552type Cache = Record<string, CacheFileName>;
553export let cache: Cache = {};
554export const hotReloadSupportFiles: Set<string> = new Set();
555export const shouldResolvedFiles: Set<string> = new Set();
556export const appComponentCollection: Map<string, Set<string>> = new Map();
557const allResolvedModules: Set<string> = new Set();
558// all files of tsc and rollup for obfuscation scanning.
559export const allSourceFilePaths: Set<string> = new Set();
560// Used to collect file paths that have not been converted toUnixPath.
561export const allModuleIds: Set<string> = new Set();
562export let props: Set<string> = new Set();
563
564export let fastBuildLogger = null;
565
566export const checkerResult: CheckerResult = { count: 0 };
567export const warnCheckerResult: WarnCheckerResult = { count: 0 };
568export let languageService: ts.LanguageService = null;
569let tsImportSendable: boolean = false;
570let skipOhModulesLint: boolean = false;
571let mixCompile: boolean = false;
572export let maxMemoryInServiceChecker: number = 0;
573export function serviceChecker(rootFileNames: string[], newLogger: Object = null, resolveModulePaths: string[] = null,
574  parentEvent?: CompileEvent, rollupShareObject?: Object): void {
575  fastBuildLogger = newLogger;
576  let cacheFile: string = null;
577  tsImportSendable = rollupShareObject?.projectConfig.tsImportSendable;
578  skipOhModulesLint = rollupShareObject?.projectConfig.skipOhModulesLint;
579  mixCompile = rollupShareObject?.projectConfig.mixCompile;
580  if (projectConfig.xtsMode || process.env.watchMode === 'true') {
581    if (projectConfig.hotReload) {
582      rootFileNames.forEach(fileName => {
583        hotReloadSupportFiles.add(fileName);
584      });
585    }
586    const recordInfo = MemoryMonitor.recordStage(MemoryDefine.CREATE_LANGUAGE_SERVICE);
587    languageService = createLanguageService(rootFileNames, resolveModulePaths, parentEvent);
588    MemoryMonitor.stopRecordStage(recordInfo);
589    props = languageService.getProps();
590  } else {
591    cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache');
592    const [isJsonObject, cacheJsonObject]: [boolean, WholeCache | undefined] = isJsonString(cacheFile);
593    const wholeCache: WholeCache = isJsonObject ? cacheJsonObject : { 'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {} };
594    if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) {
595      cache = wholeCache.fileList;
596    } else {
597      cache = {};
598    }
599    const recordInfo = MemoryMonitor.recordStage(MemoryDefine.CREATE_LANGUAGE_SERVICE);
600    languageService = createLanguageService(rootFileNames, resolveModulePaths, parentEvent, rollupShareObject);
601    MemoryMonitor.stopRecordStage(recordInfo);
602  }
603
604  const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance();
605  timePrinterInstance.setArkTSTimePrintSwitch(false);
606  timePrinterInstance.appendTime(ts.TimePhase.START);
607  ts.PerformanceDotting.startAdvanced('createProgram');
608  const recordInfo = MemoryMonitor.recordStage(MemoryDefine.GET_BUILDER_PROGRAM);
609
610  globalProgram.builderProgram = languageService.getBuilderProgram(/*withLinterProgram*/ true);
611  globalProgram.program = globalProgram.builderProgram.getProgram();
612  traverseProgramSourceFiles(languageService.getProps());
613  props = languageService.getProps();
614  timePrinterInstance.appendTime(ts.TimePhase.GET_PROGRAM);
615  MemoryMonitor.stopRecordStage(recordInfo);
616  ts.PerformanceDotting.stopAdvanced('createProgram');
617
618  collectAllFiles(globalProgram.program, undefined, undefined, rollupShareObject);
619  collectFileToIgnoreDiagnostics(rootFileNames);
620  ts.PerformanceDotting.startAdvanced('runArkTSLinterTime');
621  const runArkTSLinterRecordInfo = MemoryMonitor.recordStage(MemoryDefine.RUN_ARK_TS_LINTER);
622  const errorCodeLogger: Object | undefined = !!rollupShareObject?.getHvigorConsoleLogger ?
623    rollupShareObject?.getHvigorConsoleLogger(LINTER_SUBSYSTEM_CODE) : undefined;
624  runArkTSLinter(errorCodeLogger, parentEvent);
625  MemoryMonitor.stopRecordStage(runArkTSLinterRecordInfo);
626  ts.PerformanceDotting.stopAdvanced('runArkTSLinterTime');
627
628  if (process.env.watchMode !== 'true') {
629    const processBuildHaprrecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP);
630    processBuildHap(cacheFile, rootFileNames, parentEvent, rollupShareObject);
631    MemoryMonitor.stopRecordStage(processBuildHaprrecordInfo);
632  }
633
634  maxMemoryInServiceChecker = process.memoryUsage().heapUsed;
635  // Release the typeChecker early and perform GC in the following scenarios:
636  // In memory-priority mode or default mode, when the preview mode is disabled in a full compilation scenario,
637  // and it is not a preview, hot reload, or cold reload scenario. The typeChecker is not released early in performance-priority mode.
638  let shouldReleaseTypeChecker: boolean = rollupShareObject?.projectConfig?.executionMode !== 'performance' && globalProgram.program &&
639    process.env.watchMode !== 'true' && !projectConfig.isPreview && !projectConfig.hotReload && !projectConfig.coldReload;
640  if (shouldReleaseTypeChecker) {
641    globalProgram.program.releaseTypeChecker();
642    const allowGC: boolean = global && global.gc && typeof global.gc === 'function';
643    if (allowGC) {
644      global.gc();
645    }
646  }
647}
648
649export function traverseProgramSourceFiles(props: Set<string>): void {
650  globalProgram.program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => {
651    checkUISyntax(sourceFile, sourceFile.fileName, [], props);
652  });
653}
654
655function isJsonString(cacheFile: string): [boolean, WholeCache | undefined] {
656  if (fs.existsSync(cacheFile)) {
657    try {
658      return [true, JSON.parse(fs.readFileSync(cacheFile).toString())];
659    } catch (e) {
660      return [false, undefined];
661    }
662  } else {
663    return [false, undefined];
664  }
665}
666
667// collect the compiled files of tsc and rollup for obfuscation scanning.
668export function collectAllFiles(program?: ts.Program, rollupFileList?: IterableIterator<string>,
669  rollupObject?: Object, rollupShareObject: Object = null): void {
670  if (program) {
671    collectTscFiles(program, rollupShareObject);
672    return;
673  }
674  mergeRollUpFiles(rollupFileList, rollupObject);
675}
676
677export function collectTscFiles(program: ts.Program, rollupShareObject: Object = null): void {
678  const programAllFiles: readonly ts.SourceFile[] = program.getSourceFiles();
679  let projectRootPath: string = projectConfig.projectRootPath;
680  if (!projectRootPath) {
681    return;
682  }
683  projectRootPath = toUnixPath(projectRootPath);
684  const isMacOrWin = isWindows() || isMac();
685  const recordInfo = MemoryMonitor.recordStage(MemoryDefine.COLLECT_TSC_FILES_ALL_RESOLVED_MODULES);
686  programAllFiles.forEach(sourceFile => {
687    const fileName = toUnixPath(sourceFile.fileName);
688    // @ts-ignore
689    sourceFileDependencies.set(fileName, sourceFile.resolvedModules);
690    if (!(fileName.startsWith(projectRootPath + '/') || isOtherProjectResolvedModulesFilePaths(rollupShareObject, fileName))) {
691      return;
692    }
693    allSourceFilePaths.add(fileName);
694    allModuleIds.add(sourceFile.fileName);
695    // For the windos and mac platform, the file path in filesBuildInfo is in lowercase,
696    // while fileName of sourceFile is the original path (including uppercase).
697    if (filesBuildInfo.size > 0 &&
698      Reflect.get(sourceFile, 'version') !== filesBuildInfo.get(isMacOrWin ? tryToLowerCasePath(fileName) : fileName)) {
699      allResolvedModules.add(fileName);
700    }
701  });
702  MemoryMonitor.stopRecordStage(recordInfo);
703}
704
705function isOtherProjectResolvedModulesFilePaths(rollupShareObject: Object, fileName: string): boolean {
706  if (!!rollupShareObject && rollupShareObject.projectConfig && !!rollupShareObject.projectConfig.rootPathSet) {
707    const rootPathSet: string[] | Set<string> = rollupShareObject.projectConfig.rootPathSet;
708    if (Array.isArray(rootPathSet)) {
709      for (let i = 0; i < rootPathSet.length; i++) {
710        const pathNormalization: string = toUnixPath(rootPathSet[i]) + '/';
711        if (fileName.startsWith(pathNormalization)) {
712          return true;
713        }
714      }
715    } else {
716      return false;
717    }
718  }
719  return false;
720}
721
722export function mergeRollUpFiles(rollupFileList: IterableIterator<string>, rollupObject: Object): void {
723  const recordInfo = MemoryMonitor.recordStage(MemoryDefine.MERGE_ROLL_UP_FILES_LOCAL_PACKAGE_SET);
724  for (const moduleId of rollupFileList) {
725    if (fs.existsSync(moduleId)) {
726      allSourceFilePaths.add(toUnixPath(moduleId));
727      allModuleIds.add(moduleId);
728      addLocalPackageSet(moduleId, rollupObject);
729    }
730  }
731  MemoryMonitor.stopRecordStage(recordInfo);
732}
733
734// collect the modulename or pkgname of all local modules.
735export function addLocalPackageSet(moduleId: string, rollupObject: Object): void {
736  const moduleInfo: Object = rollupObject.getModuleInfo(moduleId);
737  const metaInfo: Object = moduleInfo.meta;
738  if (metaInfo.isLocalDependency) {
739    if (projectConfig.useNormalizedOHMUrl && metaInfo.pkgName) {
740      localPackageSet.add(metaInfo.pkgName);
741    }
742    if (!projectConfig.useNormalizedOHMUrl && metaInfo.moduleName) {
743      localPackageSet.add(metaInfo.moduleName);
744    }
745  }
746}
747
748export function emitBuildInfo(): void {
749  globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile);
750}
751
752function processBuildHap(cacheFile: string, rootFileNames: string[], parentEvent: CompileEvent,
753  rollupShareObject: Object): void {
754  ts.PerformanceDotting.startAdvanced('diagnostic');
755  const semanticRecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP_GET_SEMANTIC_DIAGNOSTICS);
756  const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram
757    .getSyntacticDiagnostics()
758    .concat(globalProgram.builderProgram.getSemanticDiagnostics());
759  MemoryMonitor.stopRecordStage(semanticRecordInfo);
760  ts.PerformanceDotting.stopAdvanced('diagnostic');
761  const emitBuildRecordInfo = MemoryMonitor.recordStage(MemoryDefine.PROCESS_BUILD_HAP_EMIT_BUILD_INFO);
762  emitBuildInfo();
763  let errorCodeLogger: Object | undefined = rollupShareObject?.getHvigorConsoleLogger ?
764    rollupShareObject?.getHvigorConsoleLogger(TSC_SYSTEM_CODE) : undefined;
765
766  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
767    printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger);
768  });
769  MemoryMonitor.stopRecordStage(emitBuildRecordInfo);
770  if (!projectConfig.xtsMode) {
771    fse.ensureDirSync(projectConfig.cachePath);
772    fs.writeFileSync(cacheFile, JSON.stringify({
773      'runtimeOS': projectConfig.runtimeOS,
774      'sdkInfo': projectConfig.sdkInfo,
775      'fileList': cache
776    }, null, 2));
777  }
778  if (projectConfig.compileHar || projectConfig.compileShared) {
779    let emit: string | undefined = undefined;
780    let writeFile = (fileName: string, text: string, writeByteOrderMark: boolean): void => {
781      emit = text;
782    };
783    [...allResolvedModules, ...rootFileNames].forEach(moduleFile => {
784      if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) {
785        try {
786          if ((/\.d\.e?ts$/).test(moduleFile)) {
787            generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile),
788              projectConfig, projectConfig.modulePathMap);
789          } else if ((/\.e?ts$/).test(moduleFile)) {
790            emit = undefined;
791            let sourcefile = globalProgram.program.getSourceFile(moduleFile);
792            if (sourcefile) {
793              globalProgram.program.emit(sourcefile, writeFile, undefined, true, undefined, true);
794            }
795            if (emit) {
796              generateSourceFilesInHar(moduleFile, emit, '.d' + path.extname(moduleFile), projectConfig, projectConfig.modulePathMap);
797            }
798          }
799        } catch (err) { }
800      }
801    });
802    printDeclarationDiagnostics(errorCodeLogger);
803  }
804}
805
806function printDeclarationDiagnostics(errorCodeLogger?: Object | undefined): void {
807  globalProgram.builderProgram.getDeclarationDiagnostics().forEach((diagnostic: ts.Diagnostic) => {
808    printDiagnostic(diagnostic, ErrorCodeModule.TSC, errorCodeLogger);
809  });
810}
811
812function containFormError(message: string): boolean {
813  if (/can't support form application./.test(message)) {
814    return true;
815  }
816  return false;
817}
818
819let fileToIgnoreDiagnostics: Set<string> | undefined = undefined;
820
821function collectFileToThrowDiagnostics(file: string, fileToThrowDiagnostics: Set<string>): void {
822  const normalizedFilePath: string = path.resolve(file);
823  const unixFilePath: string = toUnixPath(file);
824  if (fileToThrowDiagnostics.has(unixFilePath)) {
825    return;
826  }
827
828  fileToThrowDiagnostics.add(unixFilePath);
829  // Although the cache object filters JavaScript files when collecting dependency relationships, we still include the
830  // filtering of JavaScript files here to avoid potential omissions.
831  if ((/\.(c|m)?js$/).test(file) ||
832    !cache[normalizedFilePath] || cache[normalizedFilePath].children.length === 0) {
833    return;
834  }
835  cache[normalizedFilePath].children.forEach(file => {
836    collectFileToThrowDiagnostics(file, fileToThrowDiagnostics);
837  });
838}
839
840export function collectFileToIgnoreDiagnostics(rootFileNames: string[]): void {
841  if (getArkTSLinterMode() === ArkTSLinterMode.NOT_USE) {
842    return;
843  }
844
845  // In watch mode, the `beforeBuild` phase will clear the parent and children fields in the cache. For files that have
846  // not been modified, the information needs to be restored using the `resolvedModuleNames` variable.
847  if (process.env.watchMode === 'true') {
848    for (let [file, resolvedModules] of resolvedModulesCache) {
849      createOrUpdateCache(resolvedModules, file);
850    }
851  }
852
853  // With arkts linter enabled, `allowJs` option is set to true, resulting JavaScript files themselves and
854  // JavaScript-referenced files are included in the tsc program and checking process,
855  // potentially introducing new errors. For instance, in scenarios where an ets file imports js file imports ts file,
856  // it’s necessary to filter out errors from ts files.
857  let fileToThrowDiagnostics: Set<string> = new Set<string>();
858  rootFileNames.forEach(file => {
859    if (!(/\.(c|m)?js$/).test(file)) {
860      collectFileToThrowDiagnostics(file, fileToThrowDiagnostics);
861    }
862  });
863
864  let resolvedTypeReferenceDirectivesFiles: Set<string> = new Set<string>();
865  globalProgram.program.getResolvedTypeReferenceDirectives().forEach(
866    (elem: ts.ResolvedTypeReferenceDirective | undefined) => {
867      elem && elem.resolvedFileName && resolvedTypeReferenceDirectivesFiles.add(elem.resolvedFileName);
868  });
869
870  const ignoreDiagnosticsRecordInfo = MemoryMonitor.recordStage(MemoryDefine.FILE_TO_IGNORE_DIAGNOSTICS);
871  fileToIgnoreDiagnostics = new Set<string>();
872  globalProgram.program.getSourceFiles().forEach(sourceFile => {
873    // Previous projects had js libraries that were available through SDK, so need to filter js-file in SDK,
874    // like: hypium library
875    sourceFile.fileName &&
876    (!isInSDK(sourceFile.fileName) || (/\.(c|m)?js$/).test(sourceFile.fileName)) &&
877    !resolvedTypeReferenceDirectivesFiles.has(sourceFile.fileName) &&
878    fileToIgnoreDiagnostics.add(toUnixPath(sourceFile.fileName));
879  });
880
881  fileToThrowDiagnostics.forEach(file => {
882    fileToIgnoreDiagnostics.delete(file);
883  });
884  MemoryMonitor.stopRecordStage(ignoreDiagnosticsRecordInfo);
885}
886
887interface MessageCollection {
888  positionMessage: string,
889  message: string,
890  logMessage: string
891}
892
893export function printDiagnostic(diagnostic: ts.Diagnostic, flag?: ErrorCodeModule, errorCodeLogger?: Object | undefined): void {
894  if (projectConfig.ignoreWarning) {
895    return;
896  }
897
898  if (fileToIgnoreDiagnostics && diagnostic.file && diagnostic.file.fileName &&
899    fileToIgnoreDiagnostics.has(toUnixPath(diagnostic.file.fileName))) {
900    return;
901  }
902
903  const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
904  if (validateError(message)) {
905    if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
906      updateErrorFileCache(diagnostic);
907    }
908
909    if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) {
910      return;
911    }
912
913    // validate whether the message matches new Extend error, and modify its level for compatibility
914    if (validateNewExtend(message)) {
915      diagnostic.category = ts.DiagnosticCategory.warning;
916    }
917    const logPrefix: string = diagnostic.category === ts.DiagnosticCategory.Error ? 'ERROR' : 'WARN';
918    const etsCheckerLogger = fastBuildLogger || logger;
919    let logMessage: string;
920    if (logPrefix === 'ERROR') {
921      checkerResult.count += 1;
922    } else {
923      warnCheckerResult.count += 1;
924    }
925    let positionMessage: string = '';
926    if (diagnostic.file) {
927      const { line, character }: ts.LineAndCharacter =
928        diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
929      positionMessage = `File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}`;
930      logMessage = `ArkTS:${logPrefix} ${positionMessage}\n ${message}\n`;
931    } else {
932      logMessage = `ArkTS:${logPrefix}: ${message}`;
933    }
934
935    if (errorCodeLogger) {
936      const msgCollection: MessageCollection = { positionMessage, message, logMessage };
937      printErrorCode(diagnostic, etsCheckerLogger, msgCollection, errorCodeLogger, flag);
938    } else {
939      if (diagnostic.category === ts.DiagnosticCategory.Error) {
940        etsCheckerLogger.error('\u001b[31m' + logMessage);
941      } else {
942        etsCheckerLogger.warn('\u001b[33m' + logMessage);
943      }
944    }
945  }
946}
947
948function printErrorCode(diagnostic: ts.Diagnostic, etsCheckerLogger: Object,
949  msgCollection: MessageCollection, errorCodeLogger: Object, flag: ErrorCodeModule | undefined): void {
950  const { positionMessage, message, logMessage } = msgCollection;
951  // If the diagnostic is not an error, log a warning and return early.
952  if (diagnostic.category !== ts.DiagnosticCategory.Error) {
953    etsCheckerLogger.warn('\u001b[33m' + logMessage);
954    return;
955  }
956
957  // Check for TSC error codes
958  if (flag === ErrorCodeModule.TSC &&
959    validateUseErrorCodeLogger(ErrorCodeModule.TSC, diagnostic.code)) {
960    const errorCode = ts.getErrorCode(diagnostic);
961    errorCodeLogger.printError(errorCode);
962    return;
963  }
964
965  // Check for LINTER error codes
966  if (flag === ErrorCodeModule.LINTER || (flag === ErrorCodeModule.TSC &&
967    validateUseErrorCodeLogger(ErrorCodeModule.LINTER, diagnostic.code))) {
968    const linterErrorInfo: HvigorErrorInfo = transfromErrorCode(diagnostic.code, positionMessage, message);
969    errorCodeLogger.printError(linterErrorInfo);
970    return;
971  }
972
973  // Check for ArkUI error codes
974  if (flag === ErrorCodeModule.UI || (flag === ErrorCodeModule.TSC &&
975    validateUseErrorCodeLogger(ErrorCodeModule.UI, diagnostic.code))) {
976    const uiErrorInfo: HvigorErrorInfo | undefined = buildErrorInfoFromDiagnostic(
977      diagnostic.code, positionMessage, message);
978    if (!uiErrorInfo) {
979      etsCheckerLogger.error('\u001b[31m' + logMessage);
980    } else {
981      errorCodeLogger.printError(uiErrorInfo);
982    }
983    return;
984  }
985
986  // If the error is not a TSC/Linter/ArkUI error, log using etsCheckerLogger
987  etsCheckerLogger.error('\u001b[31m' + logMessage);
988}
989
990function validateUseErrorCodeLogger(flag: ErrorCodeModule, code: number): boolean {
991  if (!ts.getErrorCodeArea || !ts.getErrorCode) {
992    return false;
993  }
994  if (flag === ErrorCodeModule.TSC) {
995    return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.TSC;
996  } else if (flag === ErrorCodeModule.LINTER) {
997    return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.LINTER;
998  } else if (flag === ErrorCodeModule.UI) {
999    return ts.getErrorCodeArea(code) === ts.ErrorCodeArea.UI;
1000  }
1001  return false;
1002}
1003
1004function validateError(message: string): boolean {
1005  const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/;
1006  const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/;
1007  if (matchMessage(message, props, propInfoReg) ||
1008    matchMessage(message, props, stateInfoReg)) {
1009    return false;
1010  }
1011  return true;
1012}
1013
1014
1015/**
1016 * validate whether ets diagnostic has collected the error about new Extend function
1017 * @param {string} message
1018 * @return {*}  {boolean}
1019 */
1020function validateNewExtend(message: string): boolean {
1021  const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/;
1022  if (matchMessage(message, newExtendCollection, stateInfoReg)) {
1023    return true;
1024  }
1025  return false;
1026}
1027
1028function matchMessage(message: string, nameArr: Set<string>, reg: RegExp): boolean {
1029  if (reg.test(message)) {
1030    const match: string[] = message.match(reg);
1031    if (match[1] && nameArr.has(match[1])) {
1032      return true;
1033    }
1034  }
1035  return false;
1036}
1037
1038function updateErrorFileCache(diagnostic: ts.Diagnostic): void {
1039  if (!diagnostic.file) {
1040    return;
1041  }
1042
1043  let cacheInfo: CacheFileName = cache[path.resolve(diagnostic.file.fileName)];
1044  if (cacheInfo) {
1045    cacheInfo.error = true;
1046    if (!cacheInfo.errorCodes) {
1047      cacheInfo.errorCodes = [];
1048    }
1049    cacheInfo.errorCodes.includes(diagnostic.code) || cacheInfo.errorCodes.push(diagnostic.code);
1050  }
1051}
1052
1053function filterInput(rootFileNames: string[]): string[] {
1054  return rootFileNames.filter((file: string) => {
1055    const needUpdate: NeedUpdateFlag = { flag: false };
1056    const alreadyCheckedFiles: Set<string> = new Set();
1057    checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles);
1058    if (!needUpdate.flag) {
1059      storedFileInfo.changeFiles.push(path.resolve(file));
1060    }
1061    return needUpdate.flag;
1062  });
1063}
1064
1065function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void {
1066  if (alreadyCheckedFiles.has(file)) {
1067    return;
1068  } else {
1069    alreadyCheckedFiles.add(file);
1070  }
1071
1072  if (needUpdate.flag) {
1073    return;
1074  }
1075
1076  const value: CacheFileName = cache[file];
1077  const mtimeMs: number = fs.statSync(file).mtimeMs;
1078  if (value) {
1079    if (value.error || value.mtimeMs !== mtimeMs) {
1080      needUpdate.flag = true;
1081      return;
1082    }
1083    for (let i = 0; i < value.children.length; ++i) {
1084      if (fs.existsSync(value.children[i])) {
1085        checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles);
1086      } else {
1087        needUpdate.flag = true;
1088      }
1089    }
1090  } else {
1091    cache[file] = { mtimeMs, children: [], parent: [], error: false };
1092    needUpdate.flag = true;
1093  }
1094}
1095
1096const fileExistsCache: Map<string, boolean> = new Map<string, boolean>();
1097const dirExistsCache: Map<string, boolean> = new Map<string, boolean>();
1098const moduleResolutionHost: ts.ModuleResolutionHost = {
1099  fileExists: (fileName: string): boolean => {
1100    let exists = fileExistsCache.get(fileName);
1101    if (exists === undefined) {
1102      exists = ts.sys.fileExists(fileName);
1103      fileExistsCache.set(fileName, exists);
1104    }
1105    return exists;
1106  },
1107  directoryExists: (directoryName: string): boolean => {
1108    let exists = dirExistsCache.get(directoryName);
1109    if (exists === undefined) {
1110      exists = ts.sys.directoryExists(directoryName);
1111      dirExistsCache.set(directoryName, exists);
1112    }
1113    return exists;
1114  },
1115  readFile(fileName: string): string | undefined {
1116    return ts.sys.readFile(fileName);
1117  },
1118  realpath(path: string): string {
1119    return ts.sys.realpath(path);
1120  },
1121  trace(s: string): void {
1122    console.info(s);
1123  }
1124};
1125
1126//This is only for test
1127export const moduleResolutionHostTest = moduleResolutionHost;
1128
1129export function resolveTypeReferenceDirectives(typeDirectiveNames: string[] | ts.FileReference[]): ts.ResolvedTypeReferenceDirective[] {
1130  if (typeDirectiveNames.length === 0) {
1131    return [];
1132  }
1133
1134  const resolvedTypeReferenceCache: ts.ResolvedTypeReferenceDirective[] = [];
1135  const cache: Map<string, ts.ResolvedTypeReferenceDirective> = new Map<string, ts.ResolvedTypeReferenceDirective>();
1136  const containingFile: string = path.join(projectConfig.modulePath, 'build-profile.json5');
1137
1138  for (const entry of typeDirectiveNames) {
1139    const typeName = isString(entry) ? entry : entry.fileName.toLowerCase();
1140    if (!cache.has(typeName)) {
1141      const resolvedFile = ts.resolveTypeReferenceDirective(typeName, containingFile, compilerOptions, moduleResolutionHost);
1142      if (!resolvedFile || !resolvedFile.resolvedTypeReferenceDirective) {
1143        logger.error('\u001b[31m', `ArkTS:Cannot find type definition file for: ${typeName}\n`);
1144      }
1145      const result: ts.ResolvedTypeReferenceDirective = resolvedFile.resolvedTypeReferenceDirective;
1146      cache.set(typeName, result);
1147      resolvedTypeReferenceCache.push(result);
1148    }
1149  }
1150  return resolvedTypeReferenceCache;
1151}
1152
1153// resolvedModulesCache records the files and their dependencies of program.
1154export const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map();
1155
1156export function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] {
1157  ts.PerformanceDotting.startAdvanced('resolveModuleNames');
1158  const languageVersion = FileManager.mixCompile ? FileManager.getInstance().getLanguageVersionByFilePath(containingFile).languageVersion : ARKTS_1_1;
1159  const resolvedModules: ts.ResolvedModuleFull[] = [];
1160  const cacheFileContent: ts.ResolvedModuleFull[] = resolvedModulesCache.get(path.resolve(containingFile));
1161  if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile)) ||
1162    !(cacheFileContent && cacheFileContent.length === moduleNames.length)) {
1163    for (const moduleName of moduleNames) {
1164      const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost);
1165      if (result.resolvedModule) {
1166        if (result.resolvedModule.resolvedFileName &&
1167          path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) {
1168          const resultDETSPath: string =
1169            result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS);
1170          if (ts.sys.fileExists(resultDETSPath)) {
1171            resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS));
1172          } else {
1173            resolvedModules.push(result.resolvedModule);
1174          }
1175        } else if (isMixCompile() && result.resolvedModule.resolvedFileName && /\.ets$/.test(result.resolvedModule.resolvedFileName) &&
1176          !/\.d\.ets$/.test(result.resolvedModule.resolvedFileName)) {
1177          // When result has a value and the path parsed is the source code file path of module 1.2,
1178          // the parsing result needs to be modified to the glue code path of module 1.2
1179          const queryResult = redirectToDeclFileForInterop(result.resolvedModule.resolvedFileName);
1180          if (queryResult) {
1181            resolvedModules.push(queryResult);
1182          } else {
1183            resolvedModules.push(result.resolvedModule);
1184          }
1185        } else {
1186          resolvedModules.push(result.resolvedModule);
1187        }
1188      } else if (new RegExp(`^@(${sdkConfigPrefix})\\.`, 'i').test(moduleName.trim())) {
1189        const apiPaths = sdkConfigs.flatMap(config => config.apiPath);
1190        isMixCompile() && getApiPathForInterop(apiPaths, languageVersion);
1191        const resolveModuleInfo: ResolveModuleInfo = getRealModulePath(apiPaths, moduleName, ['.d.ts', '.d.ets']);
1192        const modulePath = resolveModuleInfo.modulePath;
1193        const extension = resolveModuleInfo.isEts ? '.d.ets' : '.d.ts';
1194        const fullModuleName = moduleName + extension;
1195
1196        if (systemModules.includes(fullModuleName) && ts.sys.fileExists(modulePath)) {
1197          resolvedModules.push(getResolveModule(modulePath, extension));
1198        } else if (languageVersion === ARKTS_1_2) {
1199          resolvedModules.push(getResolveModule(modulePath, extension));
1200        } else {
1201          resolvedModules.push(null);
1202        }
1203      } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) {
1204        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
1205        if (ts.sys.fileExists(modulePath)) {
1206          resolvedModules.push(getResolveModule(modulePath, '.ets'));
1207        } else {
1208          resolvedModules.push(null);
1209        }
1210      } else if (/\.ts$/.test(moduleName)) {
1211        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
1212        if (ts.sys.fileExists(modulePath)) {
1213          resolvedModules.push(getResolveModule(modulePath, '.ts'));
1214        } else {
1215          resolvedModules.push(null);
1216        }
1217      } else {
1218        const aliasConfig = FileManager.getInstance().queryOriginApiName(moduleName, containingFile);
1219        if (aliasConfig) {
1220          const searchPaths = aliasConfig.isStatic
1221            ? Array.from(FileManager.staticSDKDeclPath)
1222            : [...new Set(sdkConfigs.flatMap(config => config.apiPath))];
1223          const resolveModuleInfo = getRealModulePath(searchPaths, aliasConfig.originalAPIName, ['.d.ts', '.d.ets']);
1224          const modulePath = resolveModuleInfo.modulePath;
1225          const extension = resolveModuleInfo.isEts ? '.d.ets' : '.d.ts';
1226          resolvedModules.push(getResolveModule(modulePath, extension));
1227        } else {
1228          const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts');
1229          const systemDETSModulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ets');
1230          const kitModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ts');
1231          const kitSystemDETSModulePath: string = path.resolve(__dirname, '../../../kits', moduleName + '.d.ets');
1232          const suffix: string = /\.js$/.test(moduleName) ? '' : '.js';
1233          const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix);
1234          const fileModulePath: string =
1235            path.resolve(__dirname, '../node_modules', moduleName + '/index.js');
1236          const DETSModulePath: string = path.resolve(path.dirname(containingFile),
1237            /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS);
1238          const arktsEvoDeclFilePath: string = isMixCompile() ?
1239            getArkTSEvoDeclFilePath({ moduleRequest: moduleName, resolvedFileName: '' }) : '';
1240          if (ts.sys.fileExists(modulePath)) {
1241            resolvedModules.push(getResolveModule(modulePath, '.d.ts'));
1242          } else if (ts.sys.fileExists(systemDETSModulePath)) {
1243            resolvedModules.push(getResolveModule(systemDETSModulePath, '.d.ets'));
1244          } else if (ts.sys.fileExists(kitModulePath)) {
1245            resolvedModules.push(getResolveModule(kitModulePath, '.d.ts'));
1246          } else if (ts.sys.fileExists(kitSystemDETSModulePath)) {
1247            resolvedModules.push(getResolveModule(kitSystemDETSModulePath, '.d.ets'));
1248          } else if (ts.sys.fileExists(jsModulePath)) {
1249            resolvedModules.push(getResolveModule(jsModulePath, '.js'));
1250          } else if (ts.sys.fileExists(fileModulePath)) {
1251            resolvedModules.push(getResolveModule(fileModulePath, '.js'));
1252          } else if (ts.sys.fileExists(DETSModulePath)) {
1253            resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets'));
1254          } else if (isMixCompile() && ts.sys.fileExists(arktsEvoDeclFilePath)) {
1255            resolvedModules.push(getResolveModule(arktsEvoDeclFilePath, '.d.ets'));
1256          } else {
1257            const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main');
1258            let DETSModulePathFromModule: string;
1259            if (srcIndex > 0) {
1260              DETSModulePathFromModule = path.resolve(
1261                projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS);
1262              if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) {
1263                resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets'));
1264              } else {
1265                resolvedModules.push(null);
1266              }
1267            } else {
1268              resolvedModules.push(null);
1269            }
1270          }
1271        }
1272      }
1273      if (projectConfig.hotReload && resolvedModules.length &&
1274        resolvedModules[resolvedModules.length - 1]) {
1275        hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName));
1276      }
1277      if (collectShouldPackedFiles(resolvedModules)) {
1278        allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName);
1279      }
1280    }
1281    if (!projectConfig.xtsMode) {
1282      createOrUpdateCache(resolvedModules, path.resolve(containingFile));
1283    }
1284    resolvedModulesCache.set(path.resolve(containingFile), resolvedModules);
1285    ts.PerformanceDotting.stopAdvanced('resolveModuleNames');
1286    return resolvedModules;
1287
1288  }
1289  ts.PerformanceDotting.stopAdvanced('resolveModuleNames');
1290  return resolvedModulesCache.get(path.resolve(containingFile));
1291}
1292
1293export interface ResolveModuleInfo {
1294  modulePath: string;
1295  isEts: boolean;
1296}
1297
1298function collectShouldPackedFiles(resolvedModules: ts.ResolvedModuleFull[]): boolean | RegExpMatchArray {
1299  return (projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] &&
1300    resolvedModules[resolvedModules.length - 1].resolvedFileName &&
1301    (path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/) ||
1302      path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/\.d\.e?ts$/) &&
1303      path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(
1304        new RegExp('\\' + path.sep + 'src' + '\\' + path.sep + 'main' + '\\' + path.sep)));
1305}
1306
1307function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void {
1308  const children: string[] = [];
1309  const error: boolean = false;
1310  resolvedModules.forEach(moduleObj => {
1311    if (moduleObj && moduleObj.resolvedFileName && /\.(ets|ts)$/.test(moduleObj.resolvedFileName)) {
1312      const file: string = path.resolve(moduleObj.resolvedFileName);
1313      const mtimeMs: number = fs.statSync(file).mtimeMs;
1314      children.push(file);
1315      const value: CacheFileName = cache[file];
1316      if (value) {
1317        value.mtimeMs = mtimeMs;
1318        value.error = error;
1319        value.parent = value.parent || [];
1320        value.parent.push(path.resolve(containingFile));
1321        value.parent = [...new Set(value.parent)];
1322      } else {
1323        cache[file] = { mtimeMs, children: [], parent: [containingFile], error };
1324      }
1325    }
1326  });
1327  cache[path.resolve(containingFile)] = {
1328    mtimeMs: fs.statSync(containingFile).mtimeMs, children,
1329    parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ?
1330      cache[path.resolve(containingFile)].parent : [], error
1331  };
1332}
1333
1334export function createWatchCompilerHost(rootFileNames: string[],
1335  reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function,
1336  isPipe: boolean = false, resolveModulePaths: string[] = null): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> {
1337  if (projectConfig.hotReload) {
1338    rootFileNames.forEach(fileName => {
1339      hotReloadSupportFiles.add(fileName);
1340    });
1341  }
1342  if (!(isPipe && process.env.compileTool === 'rollup')) {
1343    setCompilerOptions(resolveModulePaths);
1344  }
1345  // Change the buildInfo file path, or it will cover the buildInfo file created before.
1346  const buildInfoPath: string = path.resolve(projectConfig.cachePath, '..', WATCH_COMPILER_BUILD_INFO_SUFFIX);
1347  const watchCompilerOptions = {...compilerOptions, tsBuildInfoFile: buildInfoPath};
1348  const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
1349  const host = ts.createWatchCompilerHost(
1350    [...rootFileNames, ...readDeaclareFiles()], watchCompilerOptions,
1351    ts.sys, createProgram, reportDiagnostic,
1352    (diagnostic: ts.Diagnostic) => {
1353      if ([6031, 6032].includes(diagnostic.code)) {
1354        if (!isPipe) {
1355          process.env.watchTs = 'start';
1356          resetErrorCount();
1357        }
1358      }
1359      // End of compilation in watch mode flag.
1360      if ([6193, 6194].includes(diagnostic.code)) {
1361        if (!isPipe) {
1362          process.env.watchTs = 'end';
1363          if (fastBuildLogger) {
1364            fastBuildLogger.debug(TS_WATCH_END_MSG);
1365            tsWatchEmitter.emit(TS_WATCH_END_MSG);
1366          }
1367        }
1368        delayPrintLogCount();
1369      }
1370    });
1371  host.readFile = (fileName: string) => {
1372    if (!fs.existsSync(fileName)) {
1373      return undefined;
1374    }
1375    if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
1376      let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
1377      const extendFunctionInfo: extendInfo[] = [];
1378      content = instanceInsteadThis(content, fileName, extendFunctionInfo, props);
1379      return content;
1380    }
1381    return fs.readFileSync(fileName).toString();
1382  };
1383  host.resolveModuleNames = resolveModuleNames;
1384  host.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives;
1385  return host;
1386}
1387
1388export function watchChecker(rootFileNames: string[], newLogger: any = null, resolveModulePaths: string[] = null): void {
1389  fastBuildLogger = newLogger;
1390  globalProgram.watchProgram = ts.createWatchProgram(
1391    createWatchCompilerHost(rootFileNames, printDiagnostic, () => { }, () => { }, false, resolveModulePaths));
1392}
1393
1394export function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[],
1395  props: Set<string>): string {
1396  extendFunctionInfo.reverse().forEach((item) => {
1397    const subStr: string = content.substring(item.start, item.end);
1398    const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => {
1399      return item1 + item.compName + 'Instance' + item2;
1400    });
1401    content = content.slice(0, item.start) + insert + content.slice(item.end);
1402  });
1403  return content;
1404}
1405
1406export function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull {
1407  return {
1408    resolvedFileName: modulePath,
1409    isExternalLibraryImport: false,
1410    extension: type
1411  };
1412}
1413
1414export const dollarCollection: Set<string> = new Set();
1415export const extendCollection: Set<string> = new Set();
1416// a new set defined to collect @Extend functions with new format
1417const newExtendCollection: Set<string> = new Set();
1418export const importModuleCollection: Set<string> = new Set();
1419
1420function checkUISyntax(sourceFile: ts.SourceFile, fileName: string, extendFunctionInfo: extendInfo[],
1421  props: Set<string>): void {
1422  if (/\.ets$/.test(fileName) && !/\.d.ets$/.test(fileName)) {
1423    if (process.env.compileMode === 'moduleJson' ||
1424      path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) {
1425      collectComponents(sourceFile);
1426      collectionCustomizeStyles(sourceFile);
1427      parseAllNode(sourceFile, sourceFile, extendFunctionInfo);
1428      dollarCollection.forEach((item) => {
1429        props.add(item);
1430      });
1431      extendCollection.forEach((item) => {
1432        props.add(item);
1433      });
1434    }
1435  }
1436}
1437
1438function collectionCustomizeStyles(node: ts.Node): void {
1439  if ((ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) &&
1440    (isUIDecorator(node, COMPONENT_STYLES_DECORATOR) || isUIDecorator(node, COMPONENT_EXTEND_DECORATOR)) &&
1441    node.name && ts.isIdentifier(node.name)) {
1442    BUILDIN_STYLE_NAMES.add(node.name.escapedText.toString());
1443  }
1444  if (ts.isSourceFile(node)) {
1445    node.statements.forEach((item: ts.Node) => {
1446      return collectionCustomizeStyles(item);
1447    });
1448  } else if (ts.isStructDeclaration(node)) {
1449    node.members.forEach((item: ts.Node) => {
1450      return collectionCustomizeStyles(item);
1451    });
1452  }
1453}
1454
1455function isUIDecorator(node: ts.MethodDeclaration | ts.FunctionDeclaration |
1456  ts.StructDeclaration | ts.ClassDeclaration, decortorName: string): boolean {
1457  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
1458  if (decorators && decorators.length) {
1459    for (let i = 0; i < decorators.length; i++) {
1460      const originalDecortor: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
1461      if (originalDecortor === decortorName) {
1462        return true;
1463      } else {
1464        return false;
1465      }
1466    }
1467  }
1468  return false;
1469}
1470function collectComponents(node: ts.SourceFile): void {
1471  // @ts-ignore
1472  if (process.env.watchMode !== 'true' && node.identifiers && node.identifiers.size) {
1473    // @ts-ignore
1474    for (const key of node.identifiers.keys()) {
1475      if (JS_BIND_COMPONENTS.has(key)) {
1476        appComponentCollection.get(path.join(node.fileName)).add(key);
1477      }
1478    }
1479  }
1480}
1481
1482function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void {
1483  if (ts.isStructDeclaration(node)) {
1484    if (node.members) {
1485      node.members.forEach(item => {
1486        if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
1487          const propertyName: string = item.name.getText();
1488          const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
1489          if (decorators && decorators.length) {
1490            for (let i = 0; i < decorators.length; i++) {
1491              const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
1492              if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1493                dollarCollection.add('$' + propertyName);
1494              }
1495            }
1496          }
1497        }
1498      });
1499    }
1500  }
1501  if (process.env.watchMode !== 'true' && ts.isIfStatement(node)) {
1502    appComponentCollection.get(path.join(sourceFileNode.fileName)).add(COMPONENT_IF);
1503  }
1504  if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION ||
1505    (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) &&
1506    hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
1507    if (node.body && node.body.statements && node.body.statements.length) {
1508      const checkProp: ts.NodeArray<ts.Statement> = node.body.statements;
1509      checkProp.forEach((item, index) => {
1510        traverseBuild(item, index);
1511      });
1512    }
1513  }
1514  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) {
1515    if (node.body && node.body.statements && node.body.statements.length &&
1516      !isOriginalExtend(node.body)) {
1517      extendFunctionInfo.push({
1518        start: node.pos,
1519        end: node.end,
1520        compName: isExtendFunction(node, { decoratorName: '', componentName: '' })
1521      });
1522    }
1523  }
1524  ts.forEachChild(node, (child: ts.Node) => parseAllNode(child, sourceFileNode, extendFunctionInfo));
1525}
1526
1527function isForeachAndLzayForEach(node: ts.Node): boolean {
1528  return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
1529    FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] &&
1530    ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body);
1531}
1532
1533function getComponentName(node: ts.Node): string {
1534  let temp = node.expression;
1535  let name: string;
1536  while (temp) {
1537    if (ts.isIdentifier(temp) && temp.parent && (ts.isCallExpression(temp.parent) ||
1538      ts.isEtsComponentExpression(temp.parent))) {
1539      name = temp.escapedText.toString();
1540      break;
1541    }
1542    temp = temp.expression;
1543  }
1544  return name;
1545}
1546
1547function traverseBuild(node: ts.Node, index: number): void {
1548  if (ts.isExpressionStatement(node)) {
1549    const parentComponentName: string = getComponentName(node);
1550    node = node.expression;
1551    while (node) {
1552      if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
1553        ts.isIdentifier(node.expression) && !DOLLAR_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
1554        node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1555          traverseBuild(item, indexBlock);
1556        });
1557        break;
1558      } else if (isForeachAndLzayForEach(node)) {
1559        node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1560          traverseBuild(item, indexBlock);
1561        });
1562        break;
1563      } else {
1564        loopNodeFindDoubleDollar(node, parentComponentName);
1565        if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
1566          ts.isIdentifier(node.expression)) {
1567          node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
1568            traverseBuild(item, indexBlock);
1569          });
1570          break;
1571        }
1572      }
1573      node = node.expression;
1574    }
1575  } else if (ts.isIfStatement(node)) {
1576    ifInnerDollarAttribute(node);
1577  }
1578}
1579
1580function ifInnerDollarAttribute(node: ts.IfStatement): void {
1581  if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) {
1582    node.thenStatement.statements.forEach((item, indexIfBlock) => {
1583      traverseBuild(item, indexIfBlock);
1584    });
1585  }
1586  if (node.elseStatement) {
1587    elseInnerDollarAttribute(node);
1588  }
1589}
1590
1591function elseInnerDollarAttribute(node: ts.IfStatement): void {
1592  if (ts.isIfStatement(node.elseStatement) && node.elseStatement.thenStatement && ts.isBlock(node.elseStatement.thenStatement)) {
1593    traverseBuild(node.elseStatement, 0);
1594  } else if (ts.isBlock(node.elseStatement) && node.elseStatement.statements) {
1595    node.elseStatement.statements.forEach((item, indexElseBlock) => {
1596      traverseBuild(item, indexElseBlock);
1597    });
1598  }
1599}
1600
1601function isPropertiesAddDoubleDollar(node: ts.Node): boolean {
1602  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) {
1603    return true;
1604  } else if (ts.isEtsComponentExpression(node) && ts.isIdentifier(node.expression) &&
1605    DOLLAR_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
1606    return true;
1607  } else {
1608    return false;
1609  }
1610}
1611function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void {
1612  if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
1613    const argument: ts.NodeArray<ts.Node> = node.arguments;
1614    const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name;
1615    if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) {
1616      argument.forEach((item: ts.Node) => {
1617        doubleDollarCollection(item);
1618      });
1619    }
1620  } else if (isPropertiesAddDoubleDollar(node)) {
1621    node.arguments.forEach((item: ts.Node) => {
1622      if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) {
1623        item.properties.forEach((param: ts.Node) => {
1624          if (isObjectPram(param, parentComponentName)) {
1625            doubleDollarCollection(param.initializer);
1626          }
1627        });
1628      } else if (ts.isPropertyAccessExpression(item) && (handleComponentDollarBlock(node as ts.CallExpression, parentComponentName) ||
1629        STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()))) {
1630        doubleDollarCollection(item);
1631      }
1632    });
1633  }
1634}
1635
1636function handleComponentDollarBlock(node: ts.CallExpression, parentComponentName: string): boolean {
1637  return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
1638    DOLLAR_BLOCK_INTERFACE.has(parentComponentName) && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
1639    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(node.expression.escapedText.toString());
1640}
1641
1642function doubleDollarCollection(item: ts.Node): void {
1643  if (item.getText().startsWith($$)) {
1644    while (item.expression) {
1645      item = item.expression;
1646    }
1647    dollarCollection.add(item.getText());
1648  }
1649}
1650
1651function isObjectPram(param: ts.Node, parentComponentName: string): boolean {
1652  return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) &&
1653    param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
1654    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText());
1655}
1656
1657function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean {
1658  return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
1659    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) ||
1660    STYLE_ADD_DOUBLE_DOLLAR.has(propertyName);
1661}
1662
1663function processContent(source: string, id: string): string {
1664  if (fastBuildLogger) {
1665    source = visualTransform(source, id, fastBuildLogger);
1666  }
1667  source = preprocessExtend(source, extendCollection);
1668  source = preprocessNewExtend(source, newExtendCollection);
1669  return source;
1670}
1671
1672function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void {
1673  if (shouldResolvedFiles.has(file)) {
1674    return;
1675  }
1676  shouldResolvedFiles.add(file);
1677  if (cache && cache[file] && cache[file].parent) {
1678    cache[file].parent.forEach((item) => {
1679      judgeFileShouldResolved(item, shouldResolvedFiles);
1680    });
1681    cache[file].parent = [];
1682  }
1683  if (cache && cache[file] && cache[file].children) {
1684    cache[file].children.forEach((item) => {
1685      judgeFileShouldResolved(item, shouldResolvedFiles);
1686    });
1687    cache[file].children = [];
1688  }
1689}
1690
1691export function incrementWatchFile(watchModifiedFiles: string[],
1692  watchRemovedFiles: string[]): void {
1693  const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles];
1694  if (changedFiles.length) {
1695    shouldResolvedFiles.clear();
1696  }
1697  changedFiles.forEach((file) => {
1698    judgeFileShouldResolved(file, shouldResolvedFiles);
1699  });
1700}
1701
1702export function runArkTSLinter(errorCodeLogger?: Object | undefined, parentEvent?: CompileEvent): void {
1703  const originProgram: ts.BuilderProgram = globalProgram.builderProgram;
1704
1705  const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance();
1706
1707  const arkTSLinterDiagnostics = doArkTSLinter(getArkTSVersion(),
1708    getArkTSLinterMode(),
1709    originProgram,
1710    printArkTSLinterDiagnostic,
1711    !projectConfig.xtsMode,
1712    buildInfoWriteFile,
1713    errorCodeLogger);
1714
1715  ts.PerformanceDotting.startAdvanced('updateErrorFile');
1716  if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
1717    arkTSLinterDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
1718      updateErrorFileCache(diagnostic);
1719    });
1720    timePrinterInstance.appendTime(ts.TimePhase.UPDATE_ERROR_FILE);
1721  }
1722  ts.PerformanceDotting.stopAdvanced('updateErrorFile');
1723  timePrinterInstance.printTimes();
1724  ts.ArkTSLinterTimePrinter.destroyInstance();
1725}
1726
1727function printArkTSLinterDiagnostic(diagnostic: ts.Diagnostic, errorCodeLogger?: Object | undefined): void {
1728  if (diagnostic.category === ts.DiagnosticCategory.Error && (isInOhModuleFile(diagnostic) || isEtsDeclFileInSdk(diagnostic))) {
1729    const originalCategory = diagnostic.category;
1730    diagnostic.category = ts.DiagnosticCategory.Warning;
1731    printDiagnostic(diagnostic);
1732    diagnostic.category = originalCategory;
1733    return;
1734  }
1735  printDiagnostic(diagnostic, ErrorCodeModule.LINTER, errorCodeLogger);
1736}
1737
1738function isEtsDeclFileInSdk(diagnostics: ts.Diagnostic): boolean {
1739  if (diagnostics.file?.fileName === undefined) {
1740    return false;
1741  }
1742  return isInSDK(diagnostics.file.fileName) && diagnostics.file.fileName.endsWith('.ets');
1743}
1744
1745function isInOhModuleFile(diagnostics: ts.Diagnostic): boolean {
1746  return (diagnostics.file !== undefined) &&
1747    ((diagnostics.file.fileName.indexOf('/oh_modules/') !== -1) || diagnostics.file.fileName.indexOf('\\oh_modules\\') !== -1);
1748}
1749
1750function isInSDK(fileName: string | undefined): boolean {
1751  if (projectConfig.etsLoaderPath === undefined || fileName === undefined) {
1752    return false;
1753  }
1754  const sdkPath = path.resolve(projectConfig.etsLoaderPath, '../../../');
1755  return path.resolve(fileName).startsWith(sdkPath);
1756}
1757
1758export function getArkTSLinterMode(): ArkTSLinterMode {
1759  if (!partialUpdateConfig.executeArkTSLinter) {
1760    return ArkTSLinterMode.NOT_USE;
1761  }
1762
1763  if (!partialUpdateConfig.standardArkTSLinter) {
1764    return ArkTSLinterMode.COMPATIBLE_MODE;
1765  }
1766
1767  if (isStandardMode()) {
1768    return ArkTSLinterMode.STANDARD_MODE;
1769  }
1770  return ArkTSLinterMode.COMPATIBLE_MODE;
1771}
1772
1773export function isStandardMode(): boolean {
1774  const STANDARD_MODE_COMPATIBLE_SDK_VERSION = 10;
1775  if (projectConfig &&
1776    projectConfig.compatibleSdkVersion &&
1777    projectConfig.compatibleSdkVersion >= STANDARD_MODE_COMPATIBLE_SDK_VERSION) {
1778    return true;
1779  }
1780  return false;
1781}
1782
1783function getArkTSVersion(): ArkTSVersion {
1784  if (projectConfig.arkTSVersion === '1.0') {
1785    return ArkTSVersion.ArkTS_1_0;
1786  } else if (projectConfig.arkTSVersion === '1.1') {
1787    return ArkTSVersion.ArkTS_1_1;
1788  } else if (projectConfig.arkTSVersion !== undefined) {
1789    const arkTSVersionLogger = fastBuildLogger || logger;
1790    arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version\n');
1791  }
1792
1793  if (partialUpdateConfig.arkTSVersion === '1.0') {
1794    return ArkTSVersion.ArkTS_1_0;
1795  } else if (partialUpdateConfig.arkTSVersion === '1.1') {
1796    return ArkTSVersion.ArkTS_1_1;
1797  } else if (partialUpdateConfig.arkTSVersion !== undefined) {
1798    const arkTSVersionLogger = fastBuildLogger || logger;
1799    arkTSVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid ArkTS version in metadata\n');
1800  }
1801
1802  return ArkTSVersion.ArkTS_1_1;
1803}
1804
1805enum TargetESVersion {
1806  ES2017 = 'ES2017',
1807  ES2021 = 'ES2021',
1808}
1809
1810function getTargetESVersion(): TargetESVersion {
1811  const targetESVersion = projectConfig?.projectArkOption?.tscConfig?.targetESVersion;
1812  if (targetESVersion === 'ES2017') {
1813    return TargetESVersion.ES2017;
1814  } else if (targetESVersion === 'ES2021') {
1815    return TargetESVersion.ES2021;
1816  } else if (targetESVersion !== undefined) {
1817    const targetESVersionLogger = fastBuildLogger || logger;
1818    targetESVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid Target ES version\n');
1819  }
1820  return TargetESVersion.ES2021;
1821}
1822
1823export function getMaxFlowDepth(): number {
1824  // The value of maxFlowDepth ranges from 2000 to 65535.
1825  let maxFlowDepth: number | undefined = projectConfig?.projectArkOption?.tscConfig?.maxFlowDepth;
1826
1827  if (maxFlowDepth === undefined) {
1828    maxFlowDepth = MAX_FLOW_DEPTH_DEFAULT_VALUE;
1829  } else if (maxFlowDepth < MAX_FLOW_DEPTH_DEFAULT_VALUE || maxFlowDepth > MAX_FLOW_DEPTH_MAXIMUM_VALUE) {
1830    const maxFlowDepthLogger = fastBuildLogger || logger;
1831    maxFlowDepth = MAX_FLOW_DEPTH_DEFAULT_VALUE;
1832    maxFlowDepthLogger.warn('\u001b[33m' + 'ArkTS: Invalid maxFlowDepth for control flow analysis.' +
1833      `The value of maxFlowDepth ranges from ${MAX_FLOW_DEPTH_DEFAULT_VALUE} to ${MAX_FLOW_DEPTH_MAXIMUM_VALUE}.\n` +
1834      'If the modification does not take effect, set maxFlowDepth to the default value.');
1835  }
1836  return maxFlowDepth;
1837}
1838
1839interface TargetESVersionLib {
1840  ES2017: string[],
1841  ES2021: string[],
1842}
1843
1844const targetESVersionLib: TargetESVersionLib = {
1845  // When target is es2017, the lib is es2020.
1846  ES2017: ['ES2020'],
1847  ES2021: ['ES2021'],
1848};
1849
1850function getTargetESVersionLib(): string[] {
1851  const targetESVersion = projectConfig?.projectArkOption?.tscConfig?.targetESVersion;
1852  if (targetESVersion === 'ES2017') {
1853    return targetESVersionLib.ES2017;
1854  } else if (targetESVersion === 'ES2021') {
1855    return targetESVersionLib.ES2021;
1856  } else if (targetESVersion !== undefined) {
1857    const targetESVersionLogger = fastBuildLogger || logger;
1858    targetESVersionLogger.warn('\u001b[33m' + 'ArkTS: Invalid Target ES version\n');
1859  }
1860  return targetESVersionLib.ES2021;
1861}
1862
1863function initEtsStandaloneCheckerConfig(logger, config): void {
1864  fastBuildLogger = logger;
1865  if (config.packageManagerType === 'ohpm') {
1866    config.packageDir = 'oh_modules';
1867    config.packageJson = 'oh-package.json5';
1868  } else {
1869    config.packageDir = 'node_modules';
1870    config.packageJson = 'package.json';
1871  }
1872  if (config.aceModuleJsonPath && fs.existsSync(config.aceModuleJsonPath)) {
1873    process.env.compileMode = 'moduleJson';
1874  }
1875  Object.assign(projectConfig, config);
1876}
1877
1878function resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode: string): void {
1879  resetProjectConfig();
1880  resetEtsCheck();
1881  fastBuildLogger = beforeInitFastBuildLogger;
1882  process.env.compileMode = beforeInitCompileMode;
1883}
1884
1885export function etsStandaloneChecker(entryObj, logger, projectConfig): void {
1886  const beforeInitFastBuildLogger = fastBuildLogger;
1887  const beforeInitCompileMode = process.env.compileMode;
1888  initEtsStandaloneCheckerConfig(logger, projectConfig);
1889  const rootFileNames: string[] = [];
1890  const resolveModulePaths: string[] = [];
1891  Object.values(entryObj).forEach((fileName: string) => {
1892    rootFileNames.push(path.resolve(fileName));
1893  });
1894  if (projectConfig.resolveModulePaths && Array.isArray(projectConfig.resolveModulePaths)) {
1895    resolveModulePaths.push(...projectConfig.resolveModulePaths);
1896  }
1897  const filterFiles: string[] = filterInput(rootFileNames);
1898  languageService = createLanguageService(filterFiles, resolveModulePaths);
1899  const timePrinterInstance = ts.ArkTSLinterTimePrinter.getInstance();
1900  timePrinterInstance.setArkTSTimePrintSwitch(false);
1901  timePrinterInstance.appendTime(ts.TimePhase.START);
1902  globalProgram.builderProgram = languageService.getBuilderProgram(/*withLinterProgram*/ true);
1903  globalProgram.program = globalProgram.builderProgram.getProgram();
1904  props = languageService.getProps();
1905  timePrinterInstance.appendTime(ts.TimePhase.GET_PROGRAM);
1906  collectFileToIgnoreDiagnostics(filterFiles);
1907  runArkTSLinter();
1908  const allDiagnostics: ts.Diagnostic[] = globalProgram.builderProgram
1909    .getSyntacticDiagnostics()
1910    .concat(globalProgram.builderProgram.getSemanticDiagnostics());
1911  globalProgram.builderProgram.emitBuildInfo(buildInfoWriteFile);
1912
1913  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
1914    printDiagnostic(diagnostic);
1915  });
1916  resetEtsStandaloneCheckerConfig(beforeInitFastBuildLogger, beforeInitCompileMode);
1917}
1918
1919export function resetEtsCheckTypeScript(): void {
1920  if (globalProgram.program) {
1921    globalProgram.program.releaseTypeChecker();
1922  } else if (languageService) {
1923    languageService.getProgram().releaseTypeChecker();
1924  }
1925  resetGlobalProgram();
1926  languageService = null;
1927}
1928
1929export function resetEtsCheck(): void {
1930  cache = {};
1931  props = new Set();
1932  needReCheckForChangedDepUsers = false;
1933  resetEtsCheckTypeScript();
1934  allResolvedModules.clear();
1935  checkerResult.count = 0;
1936  warnCheckerResult.count = 0;
1937  resolvedModulesCache.clear();
1938  dollarCollection.clear();
1939  extendCollection.clear();
1940  newExtendCollection.clear();
1941  allSourceFilePaths.clear();
1942  allModuleIds.clear();
1943  filesBuildInfo.clear();
1944  fileExistsCache.clear();
1945  dirExistsCache.clear();
1946  targetESVersionChanged = false;
1947  fileToIgnoreDiagnostics = undefined;
1948  maxMemoryInServiceChecker = 0;
1949}
1950