• 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';
19const fse = require('fs-extra');
20
21import {
22  projectConfig,
23  systemModules,
24  globalProgram,
25  sdkConfigs,
26  sdkConfigPrefix,
27  ohosSystemModulePaths,
28  partialUpdateConfig
29} from '../main';
30import {
31  preprocessExtend,
32  preprocessNewExtend
33} from './validate_ui_syntax';
34import {
35  INNER_COMPONENT_MEMBER_DECORATORS,
36  COMPONENT_DECORATORS_PARAMS,
37  COMPONENT_BUILD_FUNCTION,
38  STYLE_ADD_DOUBLE_DOLLAR,
39  $$,
40  PROPERTIES_ADD_DOUBLE_DOLLAR,
41  $$_BLOCK_INTERFACE,
42  COMPONENT_EXTEND_DECORATOR,
43  COMPONENT_BUILDER_DECORATOR,
44  ESMODULE,
45  EXTNAME_D_ETS,
46  EXTNAME_JS,
47  FOREACH_LAZYFOREACH,
48  COMPONENT_IF,
49  TS_WATCH_END_MSG
50} from './pre_define';
51import { getName } from './process_component_build';
52import {
53  INNER_COMPONENT_NAMES,
54  JS_BIND_COMPONENTS
55} from './component_map';
56import {
57  props,
58  logger
59} from './compile_info';
60import {
61  hasDecorator,
62  storedFileInfo
63} from './utils';
64import { generateSourceFilesInHar } from './utils';
65import { isExtendFunction, isOriginalExtend } from './process_ui_syntax';
66import { visualTransform } from './process_visual';
67import { tsWatchEmitter } from './fast_build/ets_ui/rollup-plugin-ets-checker';
68import { doArkTSLinter, ArkTSLinterMode } from './do_arkTS_linter';
69
70export const SOURCE_FILES: Map<string, ts.SourceFile> = new Map();
71
72function collectSourceFilesMap(program: ts.Program): void {
73  program.getSourceFiles().forEach((sourceFile: ts.SourceFile) => {
74    SOURCE_FILES.set(path.normalize(sourceFile.fileName), sourceFile);
75  });
76}
77
78export function readDeaclareFiles(): string[] {
79  const declarationsFileNames: string[] = [];
80  fs.readdirSync(path.resolve(__dirname, '../declarations'))
81    .forEach((fileName: string) => {
82      if (/\.d\.ts$/.test(fileName)) {
83        declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName));
84      }
85    });
86  return declarationsFileNames;
87}
88
89export const compilerOptions: ts.CompilerOptions = ts.readConfigFile(
90  path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
91function setCompilerOptions(resolveModulePaths: string[]) {
92  const allPath: Array<string> = [
93    '*'
94  ];
95  const basePath: string = path.resolve(projectConfig.projectPath);
96  if (process.env.compileTool === 'rollup' && resolveModulePaths && resolveModulePaths.length) {
97    resolveModulePaths.forEach((item: string) => {
98      if (!(/oh_modules$/.test(item) || /node_modules$/.test(item))) {
99        allPath.push(path.join(path.relative(basePath, item), '*'));
100      }
101    });
102  } else {
103    if (!projectConfig.aceModuleJsonPath) {
104      allPath.push('../../../../../*');
105      allPath.push('../../*');
106    } else {
107      allPath.push('../../../../*');
108      allPath.push('../*');
109    }
110  }
111  Object.assign(compilerOptions, {
112    'allowJs': false,
113    'emitNodeModulesFiles': true,
114    'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve,
115    'module': ts.ModuleKind.CommonJS,
116    'moduleResolution': ts.ModuleResolutionKind.NodeJs,
117    'noEmit': true,
118    'target': ts.ScriptTarget.ES2017,
119    'baseUrl': basePath,
120    'paths': {
121      '*': allPath
122    },
123    'lib': ['lib.es2020.d.ts'],
124    'types': projectConfig.compilerTypes,
125    'etsLoaderPath': projectConfig.etsLoaderPath,
126    'needDoArkTsLinter': getArkTSLinterMode() !== ArkTSLinterMode.NOT_USE,
127    'isCompatibleVersion':  getArkTSLinterMode() === ArkTSLinterMode.COMPATIBLE_MODE,
128    'skipTscOhModuleCheck': partialUpdateConfig.skipTscOhModuleCheck
129  });
130  if (projectConfig.compileMode === ESMODULE) {
131    Object.assign(compilerOptions, {
132      'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Remove,
133      'module': ts.ModuleKind.ES2020
134    });
135  }
136  if (projectConfig.packageDir === 'oh_modules') {
137    Object.assign(compilerOptions, {
138      'packageManagerType': 'ohpm'
139    });
140  }
141}
142
143interface InitCheckConfig {
144  tagName: string;
145  message: string;
146  needConditionCheck: boolean;
147  type: ts.DiagnosticCategory;
148  specifyCheckConditionFuncName: string;
149  tagNameShouldExisted: boolean;
150}
151
152interface CheckJSDocTagNameConfig {
153  needCheck: boolean;
154  checkConfig: InitCheckConfig[];
155}
156
157function getInitCheckConfig(tagName: string, message: string): InitCheckConfig {
158  return {
159    tagName: tagName,
160    message: message,
161    needConditionCheck: false,
162    type: ts.DiagnosticCategory.Error,
163    specifyCheckConditionFuncName: '',
164    tagNameShouldExisted: true
165  };
166}
167
168function getCheckJSDocTagNameConfig(fileName: string, sourceFileName: string): CheckJSDocTagNameConfig {
169  let needCheckResult: boolean = false;
170  const checkConfigArray: InitCheckConfig[] = [];
171  if (ohosSystemModulePaths.includes(path.normalize(sourceFileName)) || isArkuiDependence(sourceFileName)) {
172    if (isCardFile(fileName)) {
173      needCheckResult = true;
174      checkConfigArray.push(getInitCheckConfig('form', "'{0}' can't support form application."));
175    }
176    if (projectConfig.isCrossplatform) {
177      needCheckResult = true;
178      checkConfigArray.push(getInitCheckConfig('crossplatform', "'{0}' can't support crossplatform application."));
179    }
180  }
181
182  return {
183    needCheck: needCheckResult,
184    checkConfig: checkConfigArray
185  };
186}
187
188interface extendInfo {
189  start: number,
190  end: number,
191  compName: string
192}
193
194export const files: ts.MapLike<{ version: number }> = {};
195
196export function createLanguageService(rootFileNames: string[], resolveModulePaths: string[]): ts.LanguageService {
197  setCompilerOptions(resolveModulePaths);
198  rootFileNames.forEach((fileName: string) => {
199    files[fileName] = {version: 0};
200  });
201  const servicesHost: ts.LanguageServiceHost = {
202    getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()],
203    getScriptVersion: fileName => {
204      if (!files[path.resolve(fileName)]) {
205        files[path.resolve(fileName)] = {version: 0};
206      }
207      return files[path.resolve(fileName)].version.toString();
208    },
209    getScriptSnapshot: fileName => {
210      if (!fs.existsSync(fileName)) {
211        return undefined;
212      }
213      if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
214        appComponentCollection.set(path.join(fileName), new Set());
215        let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
216        const extendFunctionInfo: extendInfo[] = [];
217        content = instanceInsteadThis(content, fileName, extendFunctionInfo);
218        return ts.ScriptSnapshot.fromString(content);
219      }
220      return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString());
221    },
222    getCurrentDirectory: () => process.cwd(),
223    getCompilationSettings: () => compilerOptions,
224    getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
225    fileExists: ts.sys.fileExists,
226    readFile: ts.sys.readFile,
227    readDirectory: ts.sys.readDirectory,
228    resolveModuleNames: resolveModuleNames,
229    resolveTypeReferenceDirectives: resolveTypeReferenceDirectives,
230    directoryExists: ts.sys.directoryExists,
231    getDirectories: ts.sys.getDirectories,
232    getTagNameNeededCheckByFile: (fileName: string, sourceFileName: string) => {
233      return getCheckJSDocTagNameConfig(fileName, sourceFileName);
234    }
235  };
236  return ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
237}
238
239interface CacheFileName {
240  mtimeMs: number,
241  children: string[],
242  parent: string[],
243  error: boolean
244}
245interface NeedUpdateFlag {
246  flag: boolean;
247}
248interface CheckerResult {
249  count: number
250}
251
252interface WarnCheckerResult {
253  count: number
254}
255
256interface WholeCache {
257  runtimeOS: string,
258  sdkInfo: string,
259  fileList: Cache
260}
261type Cache = Record<string, CacheFileName>;
262export let cache: Cache = {};
263export const hotReloadSupportFiles: Set<string> = new Set();
264export const shouldResolvedFiles: Set<string> = new Set();
265export const appComponentCollection: Map<string, Set<string>> = new Map();
266const allResolvedModules: Set<string> = new Set();
267
268export let fastBuildLogger = null;
269
270export const checkerResult: CheckerResult = {count: 0};
271export const warnCheckerResult: WarnCheckerResult = {count: 0};
272export let languageService: ts.LanguageService = null;
273export function serviceChecker(rootFileNames: string[], newLogger: any = null, resolveModulePaths: string[] = null): void {
274  fastBuildLogger = newLogger;
275  let cacheFile: string = null;
276  if (projectConfig.xtsMode || process.env.watchMode === 'true') {
277    if (projectConfig.hotReload) {
278      rootFileNames.forEach(fileName => {
279        hotReloadSupportFiles.add(fileName);
280      });
281    }
282    languageService = createLanguageService(rootFileNames, resolveModulePaths);
283  } else {
284    cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache');
285    const wholeCache: WholeCache = fs.existsSync(cacheFile) ?
286      JSON.parse(fs.readFileSync(cacheFile).toString()) :
287      {'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {}};
288    if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) {
289      cache = wholeCache.fileList;
290    } else {
291      cache = {};
292    }
293    const filterFiles: string[] = filterInput(rootFileNames);
294    languageService = createLanguageService(filterFiles, resolveModulePaths);
295  }
296  globalProgram.program = languageService.getProgram();
297  runArkTSLinter();
298  collectSourceFilesMap(globalProgram.program);
299  if (process.env.watchMode !== 'true') {
300    processBuildHap(cacheFile, rootFileNames);
301  }
302}
303
304function processBuildHap(cacheFile: string, rootFileNames: string[]): void {
305  const allDiagnostics: ts.Diagnostic[] = globalProgram.program
306    .getSyntacticDiagnostics()
307    .concat(globalProgram.program.getSemanticDiagnostics())
308    .concat(globalProgram.program.getDeclarationDiagnostics());
309  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
310    printDiagnostic(diagnostic);
311  });
312  if (!projectConfig.xtsMode) {
313    fse.ensureDirSync(projectConfig.cachePath);
314    fs.writeFileSync(cacheFile, JSON.stringify({
315      'runtimeOS': projectConfig.runtimeOS,
316      'sdkInfo': projectConfig.sdkInfo,
317      'fileList': cache
318    }, null, 2));
319  }
320  if (projectConfig.compileHar || projectConfig.compileShared) {
321    [...allResolvedModules, ...rootFileNames].forEach(moduleFile => {
322      if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) {
323        try {
324          if ((/\.d\.e?ts$/).test(moduleFile)) {
325            generateSourceFilesInHar(moduleFile, fs.readFileSync(moduleFile, 'utf-8'), path.extname(moduleFile),
326              projectConfig);
327          } else {
328            const emit: any = languageService.getEmitOutput(moduleFile, true, true);
329            if (emit.outputFiles[0]) {
330              generateSourceFilesInHar(moduleFile, emit.outputFiles[0].text, '.d' + path.extname(moduleFile),
331                projectConfig);
332            } else {
333              console.warn(this.yellow,
334                "ArkTS:WARN doesn't generate .d" + path.extname(moduleFile) + ' for ' + moduleFile, this.reset);
335            }
336          }
337        } catch (err) {}
338      }
339    });
340  }
341}
342
343function isArkuiDependence(file: string): boolean {
344  return /compiler\/declarations/.test(file) || /ets-loader\/declarations/.test(file);
345}
346
347function isCardFile(file: string): boolean {
348  for (const key in projectConfig.cardEntryObj) {
349    if (path.normalize(projectConfig.cardEntryObj[key]) === path.normalize(file)) {
350      return true;
351    }
352  }
353  return false;
354}
355
356function containFormError(message: string): boolean {
357  if (/can't support form application./.test(message)) {
358    return true;
359  }
360  return false;
361}
362
363export function printDiagnostic(diagnostic: ts.Diagnostic): void {
364  const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
365  if (validateError(message)) {
366    if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
367      updateErrorFileCache(diagnostic);
368    }
369
370    if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) {
371      return;
372    }
373
374    const logPrefix: string = diagnostic.category === ts.DiagnosticCategory.Error ? 'ERROR' : 'WARN';
375    const etsCheckerLogger = fastBuildLogger ? fastBuildLogger : logger;
376    let logMessage: string;
377    if (logPrefix === 'ERROR') {
378      checkerResult.count += 1;
379    } else {
380      warnCheckerResult.count += 1;
381    }
382    if (diagnostic.file) {
383      const { line, character }: ts.LineAndCharacter =
384        diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
385      logMessage = `ArkTS:${logPrefix} File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`;
386    } else {
387      logMessage = `ArkTS:${logPrefix}: ${message}`;
388    }
389
390    if (diagnostic.category === ts.DiagnosticCategory.Error) {
391      etsCheckerLogger.error('\u001b[31m' + logMessage);
392    } else {
393      etsCheckerLogger.warn('\u001b[33m' + logMessage);
394    }
395  }
396}
397
398function validateError(message: string): boolean {
399  const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/;
400  const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/;
401  if (matchMessage(message, props, propInfoReg) ||
402    matchMessage(message, props, stateInfoReg)) {
403    return false;
404  }
405  return true;
406}
407function matchMessage(message: string, nameArr: any, reg: RegExp): boolean {
408  if (reg.test(message)) {
409    const match: string[] = message.match(reg);
410    if (match[1] && nameArr.includes(match[1])) {
411      return true;
412    }
413  }
414  return false;
415}
416
417function updateErrorFileCache(diagnostic: ts.Diagnostic): void {
418  if (diagnostic.file && cache[path.resolve(diagnostic.file.fileName)]) {
419    cache[path.resolve(diagnostic.file.fileName)].error = true;
420  }
421}
422
423function filterInput(rootFileNames: string[]): string[] {
424  return rootFileNames.filter((file: string) => {
425    const needUpdate: NeedUpdateFlag = { flag: false };
426    const alreadyCheckedFiles: Set<string> = new Set();
427    checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles);
428    if (!needUpdate.flag) {
429      storedFileInfo.changeFiles.push(path.resolve(file));
430    }
431    return needUpdate.flag;
432  });
433}
434
435function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void {
436  if (alreadyCheckedFiles.has(file)) {
437    return;
438  } else {
439    alreadyCheckedFiles.add(file);
440  }
441
442  if (needUpdate.flag) {
443    return;
444  }
445
446  const value: CacheFileName = cache[file];
447  const mtimeMs: number = fs.statSync(file).mtimeMs;
448  if (value) {
449    if (value.error || value.mtimeMs !== mtimeMs) {
450      needUpdate.flag = true;
451      return;
452    }
453    for (let i = 0; i < value.children.length; ++i) {
454      if (fs.existsSync(value.children[i])) {
455        checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles);
456      } else {
457        needUpdate.flag = true;
458      }
459    }
460  } else {
461    cache[file] = { mtimeMs, children: [], parent: [], error: false };
462    needUpdate.flag = true;
463  }
464}
465
466const moduleResolutionHost: ts.ModuleResolutionHost = {
467  fileExists(fileName: string): boolean {
468    return ts.sys.fileExists(fileName);
469  },
470  readFile(fileName: string): string | undefined {
471    return ts.sys.readFile(fileName);
472  },
473  realpath(path: string): string {
474    return ts.sys.realpath(path);
475  },
476  trace(s: string): void {
477    console.info(s);
478  }
479}
480
481export function resolveTypeReferenceDirectives(typeDirectiveNames: string[]): ts.ResolvedTypeReferenceDirective[] {
482  if (typeDirectiveNames.length === 0) {
483    return [];
484  }
485
486  const resolvedTypeReferenceCache: ts.ResolvedTypeReferenceDirective[] = [];
487  const cache: Map<string, ts.ResolvedTypeReferenceDirective> = new Map<string, ts.ResolvedTypeReferenceDirective>();
488  const containingFile: string = path.join(projectConfig.modulePath, "build-profile.json5");
489
490  for (const typeName of typeDirectiveNames) {
491    if (!cache.has(typeName)) {
492      const resolvedFile = ts.resolveTypeReferenceDirective(typeName, containingFile, compilerOptions, moduleResolutionHost);
493      if (!resolvedFile || !resolvedFile.resolvedTypeReferenceDirective) {
494        logger.error('\u001b[31m', `ArkTS:Cannot find type definition file for: ${typeName}\n`);
495      }
496      const result: ts.ResolvedTypeReferenceDirective = resolvedFile.resolvedTypeReferenceDirective;
497      cache.set(typeName, result);
498      resolvedTypeReferenceCache.push(result);
499    }
500  }
501  return resolvedTypeReferenceCache;
502}
503
504const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map();
505
506export function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] {
507  const resolvedModules: ts.ResolvedModuleFull[] = [];
508  if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile))
509    || !(resolvedModulesCache[path.resolve(containingFile)] &&
510      resolvedModulesCache[path.resolve(containingFile)].length === moduleNames.length)) {
511    for (const moduleName of moduleNames) {
512      const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, moduleResolutionHost);
513      if (result.resolvedModule) {
514        if (result.resolvedModule.resolvedFileName &&
515          path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) {
516          const resultDETSPath: string =
517            result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS);
518          if (ts.sys.fileExists(resultDETSPath)) {
519            resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS));
520          } else {
521            resolvedModules.push(result.resolvedModule);
522          }
523        } else {
524          resolvedModules.push(result.resolvedModule);
525        }
526      } else if (new RegExp(`^@(${sdkConfigPrefix})\\.`, 'i').test(moduleName.trim())) {
527        let apiFileExist: boolean = false;
528        for (let i = 0; i < sdkConfigs.length; i++) {
529          const sdkConfig = sdkConfigs[i];
530          let modulePath: string = path.resolve(sdkConfig.apiPath, moduleName + '.d.ts');
531          let isDETS: boolean = false;
532          if (!fs.existsSync(modulePath)) {
533            modulePath = path.resolve(sdkConfig.apiPath, moduleName + '.d.ets');
534            isDETS = true;
535          }
536          if (systemModules.includes(moduleName + (isDETS ? '.d.ets' : '.d.ts')) && ts.sys.fileExists(modulePath)) {
537            resolvedModules.push(getResolveModule(modulePath, isDETS ? '.d.ets' : '.d.ts'));
538            apiFileExist = true;
539            break;
540          }
541        }
542        if (!apiFileExist) {
543          resolvedModules.push(null);
544        }
545      } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) {
546        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
547        if (ts.sys.fileExists(modulePath)) {
548          resolvedModules.push(getResolveModule(modulePath, '.ets'));
549        } else {
550          resolvedModules.push(null);
551        }
552      } else if (/\.ts$/.test(moduleName)) {
553        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
554        if (ts.sys.fileExists(modulePath)) {
555          resolvedModules.push(getResolveModule(modulePath, '.ts'));
556        } else {
557          resolvedModules.push(null);
558        }
559      } else {
560        const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts');
561        const systemDETSModulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ets');
562        const suffix: string = /\.js$/.test(moduleName) ? '' : '.js';
563        const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix);
564        const fileModulePath: string =
565          path.resolve(__dirname, '../node_modules', moduleName + '/index.js');
566        const DETSModulePath: string = path.resolve(path.dirname(containingFile),
567          /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS);
568        if (ts.sys.fileExists(modulePath)) {
569          resolvedModules.push(getResolveModule(modulePath, '.d.ts'));
570        } else if (ts.sys.fileExists(systemDETSModulePath)) {
571          resolvedModules.push(getResolveModule(systemDETSModulePath, '.d.ets'));
572        } else if (ts.sys.fileExists(jsModulePath)) {
573          resolvedModules.push(getResolveModule(jsModulePath, '.js'));
574        } else if (ts.sys.fileExists(fileModulePath)) {
575          resolvedModules.push(getResolveModule(fileModulePath, '.js'));
576        } else if (ts.sys.fileExists(DETSModulePath)) {
577          resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets'));
578        } else {
579          const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main');
580          let DETSModulePathFromModule: string;
581          if (srcIndex > 0) {
582            DETSModulePathFromModule = path.resolve(
583              projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS);
584            if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) {
585              resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets'));
586            } else {
587              resolvedModules.push(null);
588            }
589          } else {
590            resolvedModules.push(null);
591          }
592        }
593      }
594      if (projectConfig.hotReload && resolvedModules.length &&
595        resolvedModules[resolvedModules.length - 1]) {
596        hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName));
597      }
598      if (collectShouldPackedFiles(resolvedModules)) {
599        allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName);
600      }
601    }
602    if (!projectConfig.xtsMode) {
603      createOrUpdateCache(resolvedModules, path.resolve(containingFile));
604    }
605    resolvedModulesCache[path.resolve(containingFile)] = resolvedModules;
606    return resolvedModules;
607  }
608  return resolvedModulesCache[path.resolve(containingFile)];
609}
610
611function collectShouldPackedFiles(resolvedModules: ts.ResolvedModuleFull[]): boolean | RegExpMatchArray {
612  return (projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] &&
613    resolvedModules[resolvedModules.length - 1].resolvedFileName &&
614    (path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/) ||
615    path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/\.d\.e?ts$/) &&
616    path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(
617      new RegExp('\\' + path.sep + 'src' + '\\' + path.sep + 'main' + '\\' + path.sep)));
618}
619
620function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void {
621  const children: string[] = [];
622  const error: boolean = false;
623  resolvedModules.forEach(moduleObj => {
624    if (moduleObj && moduleObj.resolvedFileName && /(?<!\.d)\.(ets|ts)$/.test(moduleObj.resolvedFileName)) {
625      const file: string = path.resolve(moduleObj.resolvedFileName);
626      const mtimeMs: number = fs.statSync(file).mtimeMs;
627      children.push(file);
628      const value: CacheFileName = cache[file];
629      if (value) {
630        value.mtimeMs = mtimeMs;
631        value.error = error;
632        value.parent = value.parent || [];
633        value.parent.push(path.resolve(containingFile));
634        value.parent = [...new Set(value.parent)];
635      } else {
636        cache[file] = { mtimeMs, children: [], parent: [containingFile], error };
637      }
638    }
639  });
640  cache[path.resolve(containingFile)] = { mtimeMs: fs.statSync(containingFile).mtimeMs, children,
641    parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ?
642      cache[path.resolve(containingFile)].parent : [], error };
643}
644
645export function createWatchCompilerHost(rootFileNames: string[],
646  reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function,
647  isPipe: boolean = false, resolveModulePaths: string[] = null): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> {
648  if (projectConfig.hotReload) {
649    rootFileNames.forEach(fileName => {
650      hotReloadSupportFiles.add(fileName);
651    });
652  }
653  setCompilerOptions(resolveModulePaths);
654  const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
655  const host = ts.createWatchCompilerHost(
656    [...rootFileNames, ...readDeaclareFiles()], compilerOptions,
657    ts.sys, createProgram, reportDiagnostic,
658    (diagnostic: ts.Diagnostic) => {
659      if ([6031, 6032].includes(diagnostic.code)) {
660        if (!isPipe) {
661          process.env.watchTs = 'start';
662          resetErrorCount();
663        }
664      }
665      // End of compilation in watch mode flag.
666      if ([6193, 6194].includes(diagnostic.code)) {
667        if (!isPipe) {
668          process.env.watchTs = 'end';
669          if (fastBuildLogger) {
670            fastBuildLogger.debug(TS_WATCH_END_MSG);
671            tsWatchEmitter.emit(TS_WATCH_END_MSG);
672          }
673        }
674        delayPrintLogCount();
675      }
676    });
677  host.readFile = (fileName: string) => {
678    if (!fs.existsSync(fileName)) {
679      return undefined;
680    }
681    if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
682      let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
683      const extendFunctionInfo: extendInfo[] = [];
684      content = instanceInsteadThis(content, fileName, extendFunctionInfo);
685      return content;
686    }
687    return fs.readFileSync(fileName).toString();
688  };
689  host.resolveModuleNames = resolveModuleNames;
690  host.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives;
691  return host;
692}
693
694export function watchChecker(rootFileNames: string[], newLogger: any = null, resolveModulePaths: string[] = null): void {
695  fastBuildLogger = newLogger;
696  globalProgram.watchProgram = ts.createWatchProgram(
697    createWatchCompilerHost(rootFileNames, printDiagnostic, () => {}, () => {}, false, resolveModulePaths));
698}
699
700export function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[]): string {
701  checkUISyntax(content, fileName, extendFunctionInfo);
702  extendFunctionInfo.reverse().forEach((item) => {
703    const subStr: string = content.substring(item.start, item.end);
704    const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => {
705      return item1 + item.compName + 'Instance' + item2;
706    });
707    content = content.slice(0, item.start) + insert + content.slice(item.end);
708  });
709  return content;
710}
711
712function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull {
713  return {
714    resolvedFileName: modulePath,
715    isExternalLibraryImport: false,
716    extension: type
717  };
718}
719
720export const dollarCollection: Set<string> = new Set();
721export const decoratorParamsCollection: Set<string> = new Set();
722export const extendCollection: Set<string> = new Set();
723export const importModuleCollection: Set<string> = new Set();
724
725function checkUISyntax(source: string, fileName: string, extendFunctionInfo: extendInfo[]): void {
726  if (/\.ets$/.test(fileName)) {
727    if (process.env.compileMode === 'moduleJson' ||
728      path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) {
729      const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source,
730        ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
731      collectComponents(sourceFile);
732      parseAllNode(sourceFile, sourceFile, extendFunctionInfo);
733      props.push(...dollarCollection, ...decoratorParamsCollection, ...extendCollection);
734    }
735  }
736}
737
738function collectComponents(node: ts.SourceFile): void {
739  // @ts-ignore
740  if (process.env.watchMode !== 'true' && node.identifiers && node.identifiers.size) {
741    // @ts-ignore
742    for (const key of node.identifiers.keys()) {
743      if (JS_BIND_COMPONENTS.has(key)) {
744        appComponentCollection.get(path.join(node.fileName)).add(key);
745      }
746    }
747  }
748}
749
750function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void {
751  if (ts.isStructDeclaration(node)) {
752    if (node.members) {
753      node.members.forEach(item => {
754        if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
755          const propertyName: string = item.name.getText();
756          if (item.decorators && item.decorators.length) {
757            for (let i = 0; i < item.decorators.length; i++) {
758              const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim();
759              if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
760                dollarCollection.add('$' + propertyName);
761              }
762              if (isDecoratorCollection(item.decorators[i], decoratorName)) {
763                decoratorParamsCollection.add(item.decorators[i].expression.arguments[0].getText());
764              }
765            }
766          }
767        }
768      });
769    }
770  }
771  if (process.env.watchMode !== 'true' && ts.isIfStatement(node)) {
772    appComponentCollection.get(path.join(sourceFileNode.fileName)).add(COMPONENT_IF);
773  }
774  if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION ||
775    (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) &&
776    hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
777    if (node.body && node.body.statements && node.body.statements.length) {
778      const checkProp: ts.NodeArray<ts.Statement> = node.body.statements;
779      checkProp.forEach((item, index) => {
780        traverseBuild(item, index);
781      });
782    }
783  }
784  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) {
785    if (node.body && node.body.statements && node.body.statements.length &&
786      !isOriginalExtend(node.body)) {
787      extendFunctionInfo.push({
788        start: node.pos,
789        end: node.end,
790        compName: isExtendFunction(node, { decoratorName: '', componentName: '' })});
791    }
792  }
793  node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode, extendFunctionInfo));
794}
795
796function isForeachAndLzayForEach(node: ts.Node): boolean {
797  return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
798    FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] &&
799    ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body);
800}
801
802function traverseBuild(node: ts.Node, index: number): void {
803  if (ts.isExpressionStatement(node)) {
804    let parentComponentName: string = getName(node);
805    if (!INNER_COMPONENT_NAMES.has(parentComponentName) && node.parent && node.parent.statements && index >= 1 &&
806      node.parent.statements[index - 1].expression && node.parent.statements[index - 1].expression.expression) {
807      parentComponentName = node.parent.statements[index - 1].expression.expression.escapedText;
808    }
809    node = node.expression;
810    if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
811      ts.isIdentifier(node.expression) && !$$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
812      node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
813        traverseBuild(item, indexBlock);
814      });
815    } else if (isForeachAndLzayForEach(node)) {
816      node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => {
817        traverseBuild(item, indexBlock);
818      });
819    } else {
820      loopNodeFindDoubleDollar(node, parentComponentName);
821      if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
822      ts.isIdentifier(node.expression)) {
823        node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
824          traverseBuild(item, indexBlock);
825        });
826      }
827    }
828  } else if (ts.isIfStatement(node)) {
829    if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) {
830      node.thenStatement.statements.forEach((item, indexIfBlock) => {
831        traverseBuild(item, indexIfBlock);
832      });
833    }
834    if (node.elseStatement && ts.isBlock(node.elseStatement) && node.elseStatement.statements) {
835      node.elseStatement.statements.forEach((item, indexElseBlock) => {
836        traverseBuild(item, indexElseBlock);
837      });
838    }
839  }
840}
841
842function isPropertiesAddDoubleDollar(node: ts.Node): boolean {
843  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) {
844    return true;
845  } else if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
846    ts.isIdentifier(node.expression) && $$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
847    return true;
848  } else {
849    return false;
850  }
851}
852function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void {
853  while (node) {
854    if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
855      const argument: ts.NodeArray<ts.Node> = node.arguments;
856      const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name;
857      if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) {
858        argument.forEach((item: ts.Node) => {
859          doubleDollarCollection(item);
860        });
861      }
862    } else if (isPropertiesAddDoubleDollar(node)) {
863      node.arguments.forEach((item: ts.Node) => {
864        if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) {
865          item.properties.forEach((param: ts.Node) => {
866            if (isObjectPram(param, parentComponentName)) {
867              doubleDollarCollection(param.initializer);
868            }
869          });
870        }
871        if (STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()) && ts.isPropertyAccessExpression(item)) {
872          doubleDollarCollection(item);
873        }
874      });
875    }
876    node = node.expression;
877  }
878}
879
880function doubleDollarCollection(item: ts.Node): void {
881  if (item.getText().startsWith($$)) {
882    while (item.expression) {
883      item = item.expression;
884    }
885    dollarCollection.add(item.getText());
886  }
887}
888
889function isObjectPram(param: ts.Node, parentComponentName:string): boolean {
890  return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) &&
891    param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
892    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText());
893}
894
895function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean {
896  return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
897    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) ||
898    STYLE_ADD_DOUBLE_DOLLAR.has(propertyName);
899}
900
901function isDecoratorCollection(item: ts.Decorator, decoratorName: string): boolean {
902  return COMPONENT_DECORATORS_PARAMS.has(decoratorName) &&
903    // @ts-ignore
904    item.expression.arguments && item.expression.arguments.length &&
905    // @ts-ignore
906    ts.isIdentifier(item.expression.arguments[0]);
907}
908
909function processContent(source: string, id: string): string {
910  if (fastBuildLogger) {
911    source = visualTransform(source, id, fastBuildLogger);
912  }
913  source = preprocessExtend(source, extendCollection);
914  source = preprocessNewExtend(source, extendCollection);
915  return source;
916}
917
918function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void {
919  if (shouldResolvedFiles.has(file)) {
920    return;
921  }
922  shouldResolvedFiles.add(file);
923  if (cache && cache[file] && cache[file].parent) {
924    cache[file].parent.forEach((item) => {
925      judgeFileShouldResolved(item, shouldResolvedFiles);
926    });
927    cache[file].parent = [];
928  }
929  if (cache && cache[file] && cache[file].children) {
930    cache[file].children.forEach((item) => {
931      judgeFileShouldResolved(item, shouldResolvedFiles);
932    });
933    cache[file].children = [];
934  }
935}
936
937export function incrementWatchFile(watchModifiedFiles: string[],
938  watchRemovedFiles: string[]): void {
939  const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles];
940  if (changedFiles.length) {
941    shouldResolvedFiles.clear();
942  }
943  changedFiles.forEach((file) => {
944    judgeFileShouldResolved(file, shouldResolvedFiles);
945  });
946}
947
948function runArkTSLinter(): void {
949  const arkTSLinterDiagnostics =
950    doArkTSLinter(globalProgram.program, getArkTSLinterMode(), printArkTSLinterDiagnostic, !projectConfig.xtsMode);
951  if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
952    arkTSLinterDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
953      updateErrorFileCache(diagnostic);
954    });
955  }
956}
957
958function printArkTSLinterDiagnostic(diagnostic: ts.Diagnostic): void {
959  if (diagnostic.category === ts.DiagnosticCategory.Error && (isInOhModuleFile(diagnostic) || isInSDK(diagnostic))) {
960    const originalCategory = diagnostic.category;
961    diagnostic.category = ts.DiagnosticCategory.Warning;
962    printDiagnostic(diagnostic);
963    diagnostic.category = originalCategory;
964    return;
965  }
966  printDiagnostic(diagnostic);
967}
968
969function isInOhModuleFile(diagnostics: ts.Diagnostic): boolean {
970  return (diagnostics.file !== undefined) &&
971    ((diagnostics.file.fileName.indexOf('/oh_modules/') !== -1) || diagnostics.file.fileName.indexOf('\\oh_modules\\') !== -1);
972}
973
974function isInSDK(diagnostics: ts.Diagnostic): boolean {
975  const fileName = diagnostics.file?.fileName;
976  if (projectConfig.etsLoaderPath === undefined || fileName === undefined) {
977    return false;
978  }
979  const sdkPath = path.resolve(projectConfig.etsLoaderPath, '../../../');
980  return path.resolve(fileName).startsWith(sdkPath);
981}
982
983export function getArkTSLinterMode(): ArkTSLinterMode {
984  if (!partialUpdateConfig.executeArkTSLinter) {
985    return ArkTSLinterMode.NOT_USE;
986  }
987
988  if (!partialUpdateConfig.standardArkTSLinter) {
989    return ArkTSLinterMode.COMPATIBLE_MODE;
990  }
991
992  if (isStandardMode()) {
993    return ArkTSLinterMode.STANDARD_MODE;
994  }
995  return ArkTSLinterMode.COMPATIBLE_MODE;
996}
997
998export function isStandardMode(): boolean {
999  const STANDARD_MODE_COMPATIBLE_SDK_VERSION = 10;
1000  if (projectConfig &&
1001    projectConfig.compatibleSdkVersion &&
1002    projectConfig.compatibleSdkVersion >= STANDARD_MODE_COMPATIBLE_SDK_VERSION) {
1003    return true;
1004  }
1005  return false;
1006}
1007
1008function initEtsStandaloneCheckerConfig(logger, config): void {
1009  fastBuildLogger = logger;
1010  if (config.packageManagerType === 'ohpm') {
1011    config.packageDir = 'oh_modules';
1012    config.packageJson = 'oh-package.json5';
1013  } else {
1014    config.packageDir = 'node_modules';
1015    config.packageJson = 'package.json';
1016  }
1017  if (config.aceModuleJsonPath && fs.existsSync(config.aceModuleJsonPath)) {
1018    process.env.compileMode = 'moduleJson';
1019  }
1020  Object.assign(projectConfig, config);
1021}
1022
1023export function etsStandaloneChecker(entryObj, logger, projectConfig): void {
1024  initEtsStandaloneCheckerConfig(logger, projectConfig);
1025  const rootFileNames: string[] = [];
1026  const resolveModulePaths: string[] = [];
1027  Object.values(entryObj).forEach((fileName: string) => {
1028    rootFileNames.push(path.resolve(fileName));
1029  });
1030  if (projectConfig.resolveModulePaths && Array.isArray(projectConfig.resolveModulePaths)) {
1031    resolveModulePaths.push(...projectConfig.resolveModulePaths);
1032  }
1033  const filterFiles: string[] = filterInput(rootFileNames);
1034  languageService = createLanguageService(filterFiles, resolveModulePaths);
1035  globalProgram.program = languageService.getProgram();
1036  runArkTSLinter();
1037  const allDiagnostics: ts.Diagnostic[] = globalProgram.program
1038    .getSyntacticDiagnostics()
1039    .concat(globalProgram.program.getSemanticDiagnostics())
1040    .concat(globalProgram.program.getDeclarationDiagnostics());
1041  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
1042    printDiagnostic(diagnostic);
1043  });
1044}
1045