• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 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  LibraryTypeCallDiagnosticChecker.instance.rebuildTscDiagnostics(tscStrictDiagnostics);
80  const lintParam: LintParameter = {
81    incrementalLinterState,
82    tscStrictDiagnostics,
83    diagnostics,
84    etsLoaderPath,
85    tsImportSendableEnable,
86    program,
87    sdkOptions
88  };
89  processFiles(srcFiles, lintParam, tscStrictDiagnostics);
90  timePrinterInstance.appendTime(TimePhase.LINT);
91  if (buildInfoWriteFile) {
92    IncrementalLinterState.emitBuildInfo(buildInfoWriteFile, tscDiagnosticsLinter.getBuilderProgram());
93    timePrinterInstance.appendTime(TimePhase.EMIT_BUILD_INFO);
94  }
95  releaseResources();
96  return diagnostics;
97}
98
99function processFiles(
100  srcFiles: ts.SourceFile[],
101  lintParameter: LintParameter,
102  tscStrictDiagnostics: Map<string, ts.Diagnostic[]>
103): void {
104  for (const fileToLint of srcFiles) {
105    const scriptKind = getScriptKind(fileToLint);
106    if (scriptKind !== ts.ScriptKind.ETS && scriptKind !== ts.ScriptKind.TS) {
107      continue;
108    }
109
110    const currentDiagnostics = getDiagnostic(fileToLint, scriptKind, lintParameter, tscStrictDiagnostics);
111    lintParameter.diagnostics.push(...currentDiagnostics);
112    lintParameter.incrementalLinterState.updateDiagnostics(fileToLint, currentDiagnostics);
113  }
114}
115
116function getDiagnostic(
117  fileToLint: ts.SourceFile,
118  scriptKind: ts.ScriptKind,
119  lintParameter: LintParameter,
120  tscStrictDiagnostics: Map<string, ts.Diagnostic[]>
121): ts.Diagnostic[] {
122  let currentDiagnostics: ts.Diagnostic[] = [];
123  if (lintParameter.incrementalLinterState.isFileChanged(fileToLint)) {
124    if (scriptKind === ts.ScriptKind.ETS) {
125      const typeScriptLinter = createTypeScriptLinter(fileToLint, lintParameter, tscStrictDiagnostics);
126      typeScriptLinter.lint();
127
128      // Get list of bad nodes from the current run.
129      currentDiagnostics = lintParameter.tscStrictDiagnostics.get(path.normalize(fileToLint.fileName)) ?? [];
130      typeScriptLinter.problemsInfos.forEach((x) => {
131        return currentDiagnostics.push(translateDiag(fileToLint, x));
132      });
133    } else {
134      if (
135        path.basename(fileToLint.fileName).toLowerCase().
136          indexOf('@kit.') === 0 ||
137        ts.isOHModules(fileToLint.fileName)
138      ) {
139        return currentDiagnostics;
140      }
141
142      const isInSdk = lintParameter.etsLoaderPath ?
143        path.
144          normalize(fileToLint.fileName).
145        // @ts-expect-error : should expect method
146          indexOf(ts.resolvePath(lintParameter.etsLoaderPath, '../..')) === 0 :
147        false;
148      if (!lintParameter.tsImportSendableEnable && !isInSdk) {
149        return currentDiagnostics;
150      }
151
152      const interopTypescriptLinter = createInteropTypescriptLinter(lintParameter.program, fileToLint, lintParameter);
153      interopTypescriptLinter.lint();
154      interopTypescriptLinter.problemsInfos.forEach((x) => {
155        return currentDiagnostics.push(translateDiag(fileToLint, x));
156      });
157    }
158  } else {
159    // Get diagnostics from old run.
160    currentDiagnostics = lintParameter.incrementalLinterState.getOldDiagnostics(fileToLint);
161  }
162  return currentDiagnostics;
163}
164
165function getSrcFiles(program: ts.Program, srcFile?: ts.SourceFile): ts.SourceFile[] {
166  let srcFiles: ts.SourceFile[] = [];
167  if (srcFile) {
168    srcFiles.push(srcFile);
169  } else {
170    srcFiles = program.getSourceFiles() as ts.SourceFile[];
171  }
172  return srcFiles;
173}
174
175function createTypeScriptLinter(
176  sourceFile: ts.SourceFile,
177  lintParameter: LintParameter,
178  tscStrictDiagnostics: Map<string, ts.Diagnostic[]>
179): TypeScriptLinter {
180  TypeScriptLinter.initGlobals();
181  return new TypeScriptLinter(
182    lintParameter.program.getLinterTypeChecker(),
183    {
184      enableAutofix: lintParameter.sdkOptions?.needAutoFix,
185      useRtLogic: lintParameter.sdkOptions?.isUseRtLogic,
186      compatibleSdkVersion: lintParameter.program.getCompilerOptions().compatibleSdkVersion,
187      compatibleSdkVersionStage: lintParameter.program.getCompilerOptions().compatibleSdkVersionStage
188    },
189    sourceFile,
190    tscStrictDiagnostics
191  );
192}
193
194function createInteropTypescriptLinter(
195  program: ts.Program,
196  sourceFile: ts.SourceFile,
197  lintParameter: LintParameter
198): InteropTypescriptLinter {
199  InteropTypescriptLinter.initGlobals();
200  return new InteropTypescriptLinter(
201    program.getLinterTypeChecker(),
202    program.getCompilerOptions(),
203    {
204      etsLoaderPath: lintParameter.etsLoaderPath,
205      useRtLogic: lintParameter.sdkOptions?.isUseRtLogic
206    },
207    sourceFile
208  );
209}
210
211// Reclaim memory for Hvigor with "no-parallel" and "daemon".
212function releaseResources(): void {}
213