• 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  printArkTSLinterFAQ(diagnostics, printDiagnostic);
79}
80
81function processArkTSLinterReportAsWarning(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc,
82  shouldWriteFile: boolean): void {
83  const filePath = shouldWriteFile ? writeOutputFile(diagnostics) : undefined;
84  if (filePath === undefined) {
85    diagnostics.forEach((diagnostic: ts.Diagnostic) => {
86      const originalCategory = diagnostic.category;
87      diagnostic.category = ts.DiagnosticCategory.Warning;
88      printDiagnostic(diagnostic);
89      diagnostic.category = originalCategory;
90    });
91    printArkTSLinterFAQ(diagnostics, printDiagnostic);
92    return;
93  }
94  const logMessage = `Has ${diagnostics.length} ArkTS Linter Error. You can get the output in ${filePath}`;
95  const arkTSDiagnostic: ts.Diagnostic = {
96    file: undefined,
97    start: undefined,
98    length: undefined,
99    messageText: logMessage,
100    category: ts.DiagnosticCategory.Warning,
101    code: -1,
102    reportsUnnecessary: undefined,
103    reportsDeprecated: undefined
104  };
105  printDiagnostic(arkTSDiagnostic);
106
107  printArkTSLinterFAQ(diagnostics, printDiagnostic);
108}
109
110function writeOutputFile(diagnostics: ts.Diagnostic[]): string | undefined {
111  let filePath: string = toUnixPath(projectConfig.cachePath);
112  if (!fs.existsSync(filePath)) {
113    return undefined;
114  }
115  filePath = toUnixPath(path.join(filePath, arkTSDir));
116  if (!fs.existsSync(filePath)) {
117    fs.mkdirSync(filePath);
118  }
119  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
120  const outputInfo: OutputInfo[] = [];
121  diagnostics.forEach((diagnostic: ts.Diagnostic) => {
122    const { line, character }: ts.LineAndCharacter =
123      diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
124    outputInfo.push({
125      categoryInfo: diagnostic.category === ts.DiagnosticCategory.Error ? 'Error' : 'Warning',
126      fileName: diagnostic.file?.fileName,
127      line: line + 1,
128      character: character + 1,
129      messageText: diagnostic.messageText
130    });
131  });
132  let output: string | undefined = filePath;
133  try {
134    fs.writeFileSync(filePath, JSON.stringify(outputInfo, undefined, spaceNumBeforeJsonLine));
135  } catch {
136    output = undefined;
137  }
138  return output;
139}
140
141function removeOutputFile(): void {
142  let filePath: string = toUnixPath(projectConfig.cachePath);
143  if (!fs.existsSync(filePath)) {
144    return;
145  }
146  filePath = toUnixPath(path.join(filePath, arkTSDir));
147  if (!fs.existsSync(filePath)) {
148    return;
149  }
150  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
151  if (fs.existsSync(filePath)) {
152    fs.rmSync(filePath);
153  }
154}
155
156function printArkTSLinterFAQ(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void {
157  if (diagnostics === undefined || diagnostics.length === undefined || diagnostics.length <= 0) {
158    return;
159  }
160
161  const logMessageFAQ = 'For details about ArkTS syntax errors, see FAQs';
162  const arkTSFAQDiagnostic: ts.Diagnostic = {
163    file: undefined,
164    start: undefined,
165    length: undefined,
166    messageText: logMessageFAQ,
167    category: ts.DiagnosticCategory.Warning,
168    code: -1,
169    reportsUnnecessary: undefined,
170    reportsDeprecated: undefined
171  };
172  printDiagnostic(arkTSFAQDiagnostic);
173}
174