• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 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, softwareP
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 * as ts from 'typescript';
17import * as path from 'node:path';
18import type { ProblemInfo } from '../../lib/ProblemInfo';
19import { ProblemSeverity } from '../../lib/ProblemSeverity';
20import { TypeScriptLinter } from '../../lib/TypeScriptLinter';
21import { getTscDiagnostics } from '../../lib/ts-diagnostics/GetTscDiagnostics';
22import { ArkTSLinterTimePrinter, TimePhase } from '../ArkTSTimePrinter';
23import { getScriptKind } from '../../lib/utils/functions/GetScriptKind';
24import { SdkTSCCompiledProgram } from './SdkTSCCompiledProgram';
25import { IncrementalLinterState } from './IncrementalLinter';
26import { InteropTypescriptLinter } from '../../lib/InteropTypescriptLinter';
27import type { SdkOptions } from './SdkOptions';
28import type { LintParameter } from './LintParameter';
29import { LibraryTypeCallDiagnosticChecker } from '../../lib/utils/functions/LibraryTypeCallDiagnosticChecker';
30
31function makeDiag(
32  category: ts.DiagnosticCategory,
33  file: ts.SourceFile,
34  length: number,
35  problemInfo: ProblemInfo
36): ts.Diagnostic {
37  const code = problemInfo.ruleTag;
38  const start = problemInfo.start;
39  const messageText = problemInfo.rule;
40  return { category, code, file, start, length, messageText };
41}
42
43export function translateDiag(srcFile: ts.SourceFile, problemInfo: ProblemInfo): ts.Diagnostic {
44  const severity =
45    problemInfo.severity === ProblemSeverity.ERROR ? ts.DiagnosticCategory.Error : ts.DiagnosticCategory.Warning;
46  const length = problemInfo.end - problemInfo.start + 1;
47  return makeDiag(severity, srcFile, length, problemInfo);
48}
49
50export function runArkTSLinter(
51  tsBuilderProgram: ts.BuilderProgram,
52  srcFile?: ts.SourceFile,
53  buildInfoWriteFile?: ts.WriteFileCallback,
54  arkTSVersion?: string,
55  sdkOptions?: SdkOptions
56): ts.Diagnostic[] {
57  const diagnostics: ts.Diagnostic[] = [];
58  const tscDiagnosticsLinter = new SdkTSCCompiledProgram(tsBuilderProgram);
59  const program = tscDiagnosticsLinter.getProgram();
60  const incrementalLinterState = new IncrementalLinterState(
61    tsBuilderProgram,
62    arkTSVersion,
63    program.getCompilerOptions()
64  );
65  incrementalLinterState.updateProgramStateArkTSVersion(arkTSVersion);
66  const timePrinterInstance = ArkTSLinterTimePrinter.getInstance();
67  timePrinterInstance.appendTime(TimePhase.INIT);
68  tscDiagnosticsLinter.updateCompilationDiagnostics();
69  const srcFiles: ts.SourceFile[] = getSrcFiles(program, srcFile);
70  const tscStrictDiagnostics = getTscDiagnostics(
71    tscDiagnosticsLinter,
72    srcFiles.filter((file) => {
73      return incrementalLinterState.isFileChanged(file);
74    })
75  );
76  timePrinterInstance.appendTime(TimePhase.GET_TSC_DIAGNOSTICS);
77  const etsLoaderPath = program.getCompilerOptions().etsLoaderPath;
78  const tsImportSendableEnable = program.getCompilerOptions().tsImportSendableEnable;
79  const typeScriptLinter = createTypeScriptLinter(program, tscStrictDiagnostics, sdkOptions);
80  LibraryTypeCallDiagnosticChecker.instance.rebuildTscDiagnostics(tscStrictDiagnostics);
81  const interopTypescriptLinter = createInteropTypescriptLinter(program, !!sdkOptions?.isUseRtLogic);
82  processFiles(srcFiles, {
83    incrementalLinterState,
84    typeScriptLinter,
85    interopTypescriptLinter,
86    tscStrictDiagnostics,
87    diagnostics,
88    etsLoaderPath,
89    tsImportSendableEnable
90  });
91  timePrinterInstance.appendTime(TimePhase.LINT);
92  if (buildInfoWriteFile) {
93    IncrementalLinterState.emitBuildInfo(buildInfoWriteFile, tscDiagnosticsLinter.getBuilderProgram());
94    timePrinterInstance.appendTime(TimePhase.EMIT_BUILD_INFO);
95  }
96  releaseResources();
97  return diagnostics;
98}
99
100function processFiles(srcFiles: ts.SourceFile[], lintParameter: LintParameter): void {
101  for (const fileToLint of srcFiles) {
102    const scriptKind = getScriptKind(fileToLint);
103    if (scriptKind !== ts.ScriptKind.ETS && scriptKind !== ts.ScriptKind.TS) {
104      continue;
105    }
106
107    const currentDiagnostics = getDiagnostic(fileToLint, scriptKind, lintParameter);
108    lintParameter.diagnostics.push(...currentDiagnostics);
109    lintParameter.incrementalLinterState.updateDiagnostics(fileToLint, currentDiagnostics);
110  }
111}
112
113function getDiagnostic(
114  fileToLint: ts.SourceFile,
115  scriptKind: ts.ScriptKind,
116  lintParameter: LintParameter
117): ts.Diagnostic[] {
118  let currentDiagnostics: ts.Diagnostic[] = [];
119  if (lintParameter.incrementalLinterState.isFileChanged(fileToLint)) {
120    if (scriptKind === ts.ScriptKind.ETS) {
121      lintParameter.typeScriptLinter.lint(fileToLint);
122
123      // Get list of bad nodes from the current run.
124      currentDiagnostics = lintParameter.tscStrictDiagnostics.get(path.normalize(fileToLint.fileName)) ?? [];
125      lintParameter.typeScriptLinter.problemsInfos.forEach((x) => {
126        return currentDiagnostics.push(translateDiag(fileToLint, x));
127      });
128      lintParameter.typeScriptLinter.problemsInfos.length = 0;
129    } else {
130      if (
131        path.basename(fileToLint.fileName).toLowerCase().
132          indexOf('@kit.') === 0 ||
133        ts.isOHModules(fileToLint.fileName)
134      ) {
135        return currentDiagnostics;
136      }
137
138      const isInSdk = lintParameter.etsLoaderPath ?
139        path.
140          normalize(fileToLint.fileName).
141        // @ts-expect-error : should expect method
142          indexOf(ts.resolvePath(lintParameter.etsLoaderPath, '../..')) === 0 :
143        false;
144      if (!lintParameter.tsImportSendableEnable && !isInSdk) {
145        return currentDiagnostics;
146      }
147
148      lintParameter.interopTypescriptLinter.lint(fileToLint);
149      lintParameter.interopTypescriptLinter.problemsInfos.forEach((x) => {
150        return currentDiagnostics.push(translateDiag(fileToLint, x));
151      });
152      lintParameter.interopTypescriptLinter.problemsInfos.length = 0;
153    }
154  } else {
155    // Get diagnostics from old run.
156    currentDiagnostics = lintParameter.incrementalLinterState.getOldDiagnostics(fileToLint);
157  }
158  return currentDiagnostics;
159}
160
161function getSrcFiles(program: ts.Program, srcFile?: ts.SourceFile): ts.SourceFile[] {
162  let srcFiles: ts.SourceFile[] = [];
163  if (srcFile) {
164    srcFiles.push(srcFile);
165  } else {
166    srcFiles = program.getSourceFiles() as ts.SourceFile[];
167  }
168  return srcFiles;
169}
170
171function createTypeScriptLinter(
172  program: ts.Program,
173  tscStrictDiagnostics: Map<string, ts.Diagnostic[]>,
174  sdkOptions?: SdkOptions
175): TypeScriptLinter {
176  TypeScriptLinter.initGlobals();
177  return new TypeScriptLinter(
178    program.getLinterTypeChecker(),
179    {
180      ideMode: true,
181      enableAutofix: sdkOptions?.needAutoFix,
182      useRtLogic: sdkOptions?.isUseRtLogic,
183      compatibleSdkVersion: program.getCompilerOptions().compatibleSdkVersion,
184      compatibleSdkVersionStage: program.getCompilerOptions().compatibleSdkVersionStage
185    },
186    tscStrictDiagnostics
187  );
188}
189
190function createInteropTypescriptLinter(program: ts.Program, useRtLogic: boolean): InteropTypescriptLinter {
191  InteropTypescriptLinter.initGlobals();
192  return new InteropTypescriptLinter(
193    program.getLinterTypeChecker(),
194    program.getCompilerOptions(),
195    {
196      ideMode: true,
197      useRtLogic: useRtLogic
198    },
199    program.getCompilerOptions().etsLoaderPath
200  );
201}
202
203// Reclaim memory for Hvigor with "no-parallel" and "daemon".
204function releaseResources(): void {}
205