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