• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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 { projectConfig } from '../main';
20import { toUnixPath } from './utils';
21import {
22  resolveModuleNames,
23  resolveTypeReferenceDirectives
24} from './ets_checker'
25
26const arkTSDir: string = 'ArkTS';
27const arkTSLinterOutputFileName: string = 'ArkTSLinter_output.json';
28const spaceNumBeforeJsonLine = 2;
29
30interface OutputInfo {
31  categoryInfo: string | undefined;
32  fileName: string | undefined;
33  line: number | undefined;
34  character: number | undefined;
35  messageText: string | ts.DiagnosticMessageChain;
36}
37
38export enum ArkTSLinterMode {
39  NOT_USE = 0,
40  COMPATIBLE_MODE = 1,
41  STANDARD_MODE = 2
42}
43
44export type ProcessDiagnosticsFunc = (diagnostics: ts.Diagnostic) => void;
45
46export function doArkTSLinter(program: ts.Program, arkTSMode: ArkTSLinterMode, printDiagnostic: ProcessDiagnosticsFunc,
47  shouldWriteFile: boolean = true): ts.Diagnostic[] {
48  if (arkTSMode === ArkTSLinterMode.NOT_USE) {
49    return [];
50  }
51
52  const compilerHost: ts.CompilerHost = ts.createCompilerHost(program.getCompilerOptions());
53  compilerHost.resolveModuleNames = resolveModuleNames;
54  compilerHost.getCurrentDirectory = () => process.cwd();
55  compilerHost.getDefaultLibFileName = options => ts.getDefaultLibFilePath(options);
56  compilerHost.resolveTypeReferenceDirectives = resolveTypeReferenceDirectives;
57
58  let diagnostics: ts.Diagnostic[] = ts.runArkTSLinter(program, compilerHost);
59
60  removeOutputFile();
61  if (diagnostics.length === 0) {
62    return [];
63  }
64
65  if (arkTSMode === ArkTSLinterMode.COMPATIBLE_MODE) {
66    processArkTSLinterReportAsWarning(diagnostics, printDiagnostic, shouldWriteFile);
67  } else {
68    processArkTSLinterReportAsError(diagnostics, printDiagnostic);
69  }
70
71  return diagnostics;
72}
73
74function processArkTSLinterReportAsError(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void {
75  diagnostics.forEach((diagnostic: ts.Diagnostic) => {
76    printDiagnostic(diagnostic);
77  });
78}
79
80function processArkTSLinterReportAsWarning(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc,
81  shouldWriteFile: boolean): void {
82  const filePath = shouldWriteFile ? writeOutputFile(diagnostics) : undefined;
83  if (filePath === undefined) {
84    diagnostics.forEach((diagnostic: ts.Diagnostic) => {
85      const originalCategory = diagnostic.category;
86      diagnostic.category = ts.DiagnosticCategory.Warning;
87      printDiagnostic(diagnostic);
88      diagnostic.category = originalCategory;
89    });
90    return;
91  }
92  const logMessage = `Has ${diagnostics.length} ArkTS Linter Error. You can get the output in ${filePath}`;
93  const arkTSDiagnostic: ts.Diagnostic = {
94    file: undefined,
95    start: undefined,
96    length: undefined,
97    messageText: logMessage,
98    category: ts.DiagnosticCategory.Warning,
99    code: -1,
100    reportsUnnecessary: undefined,
101    reportsDeprecated: undefined
102  };
103  printDiagnostic(arkTSDiagnostic);
104}
105
106function writeOutputFile(diagnostics: ts.Diagnostic[]): string | undefined {
107  let filePath: string = toUnixPath(projectConfig.cachePath);
108  if (!fs.existsSync(filePath)) {
109    return undefined;
110  }
111  filePath = toUnixPath(path.join(filePath, arkTSDir));
112  if (!fs.existsSync(filePath)) {
113    fs.mkdirSync(filePath);
114  }
115  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
116  const outputInfo: OutputInfo[] = [];
117  diagnostics.forEach((diagnostic: ts.Diagnostic) => {
118    const { line, character }: ts.LineAndCharacter =
119      diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
120    outputInfo.push({
121      categoryInfo: diagnostic.category === ts.DiagnosticCategory.Error ? 'Error' : 'Warning',
122      fileName: diagnostic.file?.fileName,
123      line: line + 1,
124      character: character + 1,
125      messageText: diagnostic.messageText
126    });
127  });
128  let output: string | undefined = filePath;
129  try {
130    fs.writeFileSync(filePath, JSON.stringify(outputInfo, undefined, spaceNumBeforeJsonLine));
131  } catch {
132    output = undefined;
133  }
134  return output;
135}
136
137function removeOutputFile(): void {
138  let filePath: string = toUnixPath(projectConfig.cachePath);
139  if (!fs.existsSync(filePath)) {
140    return;
141  }
142  filePath = toUnixPath(path.join(filePath, arkTSDir));
143  if (!fs.existsSync(filePath)) {
144    return;
145  }
146  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
147  if (fs.existsSync(filePath)) {
148    fs.rmSync(filePath);
149  }
150}
151