107ac75b1Sopenharmony_ci/*
207ac75b1Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
307ac75b1Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
407ac75b1Sopenharmony_ci * you may not use this file except in compliance with the License.
507ac75b1Sopenharmony_ci * You may obtain a copy of the License at
607ac75b1Sopenharmony_ci *
707ac75b1Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
807ac75b1Sopenharmony_ci *
907ac75b1Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1007ac75b1Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1107ac75b1Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1207ac75b1Sopenharmony_ci * See the License for the specific language governing permissions and
1307ac75b1Sopenharmony_ci * limitations under the License.
1407ac75b1Sopenharmony_ci */
1507ac75b1Sopenharmony_ci
1607ac75b1Sopenharmony_ciimport fs from 'fs';
1707ac75b1Sopenharmony_ciimport path from 'path';
1807ac75b1Sopenharmony_ciimport * as ts from 'typescript';
1907ac75b1Sopenharmony_ciimport {
2007ac75b1Sopenharmony_ci  globalProgram,
2107ac75b1Sopenharmony_ci  projectConfig
2207ac75b1Sopenharmony_ci} from '../main';
2307ac75b1Sopenharmony_ciimport {
2407ac75b1Sopenharmony_ci  toUnixPath,
2507ac75b1Sopenharmony_ci  getRollupCache,
2607ac75b1Sopenharmony_ci  setRollupCache
2707ac75b1Sopenharmony_ci} from './utils';
2807ac75b1Sopenharmony_ciimport {
2907ac75b1Sopenharmony_ci  resolveModuleNames,
3007ac75b1Sopenharmony_ci  resolveTypeReferenceDirectives,
3107ac75b1Sopenharmony_ci  fileHashScriptVersion,
3207ac75b1Sopenharmony_ci  LanguageServiceCache,
3307ac75b1Sopenharmony_ci} from './ets_checker';
3407ac75b1Sopenharmony_ciimport { ARKTS_LINTER_BUILD_INFO_SUFFIX } from './pre_define';
3507ac75b1Sopenharmony_ci
3607ac75b1Sopenharmony_ciconst arkTSDir: string = 'ArkTS';
3707ac75b1Sopenharmony_ciconst arkTSLinterOutputFileName: string = 'ArkTSLinter_output.json';
3807ac75b1Sopenharmony_ciconst spaceNumBeforeJsonLine = 2;
3907ac75b1Sopenharmony_ci
4007ac75b1Sopenharmony_ciinterface OutputInfo {
4107ac75b1Sopenharmony_ci  categoryInfo: string | undefined;
4207ac75b1Sopenharmony_ci  fileName: string | undefined;
4307ac75b1Sopenharmony_ci  line: number | undefined;
4407ac75b1Sopenharmony_ci  character: number | undefined;
4507ac75b1Sopenharmony_ci  messageText: string | ts.DiagnosticMessageChain;
4607ac75b1Sopenharmony_ci}
4707ac75b1Sopenharmony_ci
4807ac75b1Sopenharmony_ciexport enum ArkTSLinterMode {
4907ac75b1Sopenharmony_ci  NOT_USE = 0,
5007ac75b1Sopenharmony_ci  COMPATIBLE_MODE = 1,
5107ac75b1Sopenharmony_ci  STANDARD_MODE = 2
5207ac75b1Sopenharmony_ci}
5307ac75b1Sopenharmony_ci
5407ac75b1Sopenharmony_ciexport enum ArkTSVersion {
5507ac75b1Sopenharmony_ci  ArkTS_1_0,
5607ac75b1Sopenharmony_ci  ArkTS_1_1,
5707ac75b1Sopenharmony_ci}
5807ac75b1Sopenharmony_ci
5907ac75b1Sopenharmony_ciexport type ProcessDiagnosticsFunc = (diagnostics: ts.Diagnostic) => void;
6007ac75b1Sopenharmony_ci
6107ac75b1Sopenharmony_cifunction getArkTSVersionString(arkTSVersion: ArkTSVersion): string {
6207ac75b1Sopenharmony_ci  return arkTSVersion === ArkTSVersion.ArkTS_1_0 ? 'ArkTS_1_0' : 'ArkTS_1_1';
6307ac75b1Sopenharmony_ci}
6407ac75b1Sopenharmony_ci
6507ac75b1Sopenharmony_ciexport function doArkTSLinter(arkTSVersion: ArkTSVersion, arkTSMode: ArkTSLinterMode,
6607ac75b1Sopenharmony_ci  builderProgram: ts.BuilderProgram, printDiagnostic: ProcessDiagnosticsFunc, shouldWriteFile: boolean = true,
6707ac75b1Sopenharmony_ci  buildInfoWriteFile?: ts.WriteFileCallback): ts.Diagnostic[] {
6807ac75b1Sopenharmony_ci  if (arkTSMode === ArkTSLinterMode.NOT_USE) {
6907ac75b1Sopenharmony_ci    return [];
7007ac75b1Sopenharmony_ci  }
7107ac75b1Sopenharmony_ci
7207ac75b1Sopenharmony_ci  let diagnostics: ts.Diagnostic[] = [];
7307ac75b1Sopenharmony_ci
7407ac75b1Sopenharmony_ci  if (arkTSVersion === ArkTSVersion.ArkTS_1_0) {
7507ac75b1Sopenharmony_ci    diagnostics = ts.ArkTSLinter_1_0.runArkTSLinter(builderProgram, /*srcFile*/ undefined, buildInfoWriteFile,
7607ac75b1Sopenharmony_ci      getArkTSVersionString(arkTSVersion));
7707ac75b1Sopenharmony_ci  } else {
7807ac75b1Sopenharmony_ci    diagnostics = ts.ArkTSLinter_1_1.runArkTSLinter(builderProgram, /*srcFile*/ undefined, buildInfoWriteFile,
7907ac75b1Sopenharmony_ci      getArkTSVersionString(arkTSVersion));
8007ac75b1Sopenharmony_ci  }
8107ac75b1Sopenharmony_ci
8207ac75b1Sopenharmony_ci  removeOutputFile();
8307ac75b1Sopenharmony_ci  if (diagnostics.length === 0) {
8407ac75b1Sopenharmony_ci    return [];
8507ac75b1Sopenharmony_ci  }
8607ac75b1Sopenharmony_ci
8707ac75b1Sopenharmony_ci  if (arkTSMode === ArkTSLinterMode.COMPATIBLE_MODE) {
8807ac75b1Sopenharmony_ci    processArkTSLinterReportAsWarning(diagnostics, printDiagnostic, shouldWriteFile);
8907ac75b1Sopenharmony_ci  } else {
9007ac75b1Sopenharmony_ci    processArkTSLinterReportAsError(diagnostics, printDiagnostic);
9107ac75b1Sopenharmony_ci  }
9207ac75b1Sopenharmony_ci
9307ac75b1Sopenharmony_ci  return diagnostics;
9407ac75b1Sopenharmony_ci}
9507ac75b1Sopenharmony_ci
9607ac75b1Sopenharmony_cifunction processArkTSLinterReportAsError(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void {
9707ac75b1Sopenharmony_ci  diagnostics.forEach((diagnostic: ts.Diagnostic) => {
9807ac75b1Sopenharmony_ci    printDiagnostic(diagnostic);
9907ac75b1Sopenharmony_ci  });
10007ac75b1Sopenharmony_ci  printArkTSLinterFAQ(diagnostics, printDiagnostic);
10107ac75b1Sopenharmony_ci}
10207ac75b1Sopenharmony_ci
10307ac75b1Sopenharmony_cifunction processArkTSLinterReportAsWarning(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc,
10407ac75b1Sopenharmony_ci  shouldWriteFile: boolean): void {
10507ac75b1Sopenharmony_ci  const filePath = shouldWriteFile ? writeOutputFile(diagnostics) : undefined;
10607ac75b1Sopenharmony_ci  if (filePath === undefined) {
10707ac75b1Sopenharmony_ci    diagnostics.forEach((diagnostic: ts.Diagnostic) => {
10807ac75b1Sopenharmony_ci      const originalCategory = diagnostic.category;
10907ac75b1Sopenharmony_ci      diagnostic.category = ts.DiagnosticCategory.Warning;
11007ac75b1Sopenharmony_ci      printDiagnostic(diagnostic);
11107ac75b1Sopenharmony_ci      diagnostic.category = originalCategory;
11207ac75b1Sopenharmony_ci    });
11307ac75b1Sopenharmony_ci    printArkTSLinterFAQ(diagnostics, printDiagnostic);
11407ac75b1Sopenharmony_ci    return;
11507ac75b1Sopenharmony_ci  }
11607ac75b1Sopenharmony_ci  const logMessage = `Has ${diagnostics.length} ArkTS Linter Error. You can get the output in ${filePath}`;
11707ac75b1Sopenharmony_ci  const arkTSDiagnostic: ts.Diagnostic = {
11807ac75b1Sopenharmony_ci    file: undefined,
11907ac75b1Sopenharmony_ci    start: undefined,
12007ac75b1Sopenharmony_ci    length: undefined,
12107ac75b1Sopenharmony_ci    messageText: logMessage,
12207ac75b1Sopenharmony_ci    category: ts.DiagnosticCategory.Warning,
12307ac75b1Sopenharmony_ci    code: -1,
12407ac75b1Sopenharmony_ci    reportsUnnecessary: undefined,
12507ac75b1Sopenharmony_ci    reportsDeprecated: undefined
12607ac75b1Sopenharmony_ci  };
12707ac75b1Sopenharmony_ci  printDiagnostic(arkTSDiagnostic);
12807ac75b1Sopenharmony_ci
12907ac75b1Sopenharmony_ci  printArkTSLinterFAQ(diagnostics, printDiagnostic);
13007ac75b1Sopenharmony_ci}
13107ac75b1Sopenharmony_ci
13207ac75b1Sopenharmony_cifunction writeOutputFile(diagnostics: ts.Diagnostic[]): string | undefined {
13307ac75b1Sopenharmony_ci  let filePath: string = toUnixPath(projectConfig.cachePath);
13407ac75b1Sopenharmony_ci  if (!fs.existsSync(filePath)) {
13507ac75b1Sopenharmony_ci    return undefined;
13607ac75b1Sopenharmony_ci  }
13707ac75b1Sopenharmony_ci  filePath = toUnixPath(path.join(filePath, arkTSDir));
13807ac75b1Sopenharmony_ci  if (!fs.existsSync(filePath)) {
13907ac75b1Sopenharmony_ci    fs.mkdirSync(filePath);
14007ac75b1Sopenharmony_ci  }
14107ac75b1Sopenharmony_ci  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
14207ac75b1Sopenharmony_ci  const outputInfo: OutputInfo[] = [];
14307ac75b1Sopenharmony_ci  diagnostics.forEach((diagnostic: ts.Diagnostic) => {
14407ac75b1Sopenharmony_ci    const { line, character }: ts.LineAndCharacter =
14507ac75b1Sopenharmony_ci      diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
14607ac75b1Sopenharmony_ci    outputInfo.push({
14707ac75b1Sopenharmony_ci      categoryInfo: diagnostic.category === ts.DiagnosticCategory.Error ? 'Error' : 'Warning',
14807ac75b1Sopenharmony_ci      fileName: diagnostic.file?.fileName,
14907ac75b1Sopenharmony_ci      line: line + 1,
15007ac75b1Sopenharmony_ci      character: character + 1,
15107ac75b1Sopenharmony_ci      messageText: diagnostic.messageText
15207ac75b1Sopenharmony_ci    });
15307ac75b1Sopenharmony_ci  });
15407ac75b1Sopenharmony_ci  let output: string | undefined = filePath;
15507ac75b1Sopenharmony_ci  try {
15607ac75b1Sopenharmony_ci    fs.writeFileSync(filePath, JSON.stringify(outputInfo, undefined, spaceNumBeforeJsonLine));
15707ac75b1Sopenharmony_ci  } catch {
15807ac75b1Sopenharmony_ci    output = undefined;
15907ac75b1Sopenharmony_ci  }
16007ac75b1Sopenharmony_ci  return output;
16107ac75b1Sopenharmony_ci}
16207ac75b1Sopenharmony_ci
16307ac75b1Sopenharmony_cifunction removeOutputFile(): void {
16407ac75b1Sopenharmony_ci  let filePath: string = toUnixPath(projectConfig.cachePath);
16507ac75b1Sopenharmony_ci  if (!fs.existsSync(filePath)) {
16607ac75b1Sopenharmony_ci    return;
16707ac75b1Sopenharmony_ci  }
16807ac75b1Sopenharmony_ci  filePath = toUnixPath(path.join(filePath, arkTSDir));
16907ac75b1Sopenharmony_ci  if (!fs.existsSync(filePath)) {
17007ac75b1Sopenharmony_ci    return;
17107ac75b1Sopenharmony_ci  }
17207ac75b1Sopenharmony_ci  filePath = toUnixPath((path.join(filePath, arkTSLinterOutputFileName)));
17307ac75b1Sopenharmony_ci  if (fs.existsSync(filePath)) {
17407ac75b1Sopenharmony_ci    fs.rmSync(filePath);
17507ac75b1Sopenharmony_ci  }
17607ac75b1Sopenharmony_ci}
17707ac75b1Sopenharmony_ci
17807ac75b1Sopenharmony_cifunction printArkTSLinterFAQ(diagnostics: ts.Diagnostic[], printDiagnostic: ProcessDiagnosticsFunc): void {
17907ac75b1Sopenharmony_ci  if (diagnostics === undefined || diagnostics.length === undefined || diagnostics.length <= 0) {
18007ac75b1Sopenharmony_ci    return;
18107ac75b1Sopenharmony_ci  }
18207ac75b1Sopenharmony_ci
18307ac75b1Sopenharmony_ci  const logMessageFAQ = 'For details about ArkTS syntax errors, see FAQs';
18407ac75b1Sopenharmony_ci  const arkTSFAQDiagnostic: ts.Diagnostic = {
18507ac75b1Sopenharmony_ci    file: undefined,
18607ac75b1Sopenharmony_ci    start: undefined,
18707ac75b1Sopenharmony_ci    length: undefined,
18807ac75b1Sopenharmony_ci    messageText: logMessageFAQ,
18907ac75b1Sopenharmony_ci    category: ts.DiagnosticCategory.Warning,
19007ac75b1Sopenharmony_ci    code: -1,
19107ac75b1Sopenharmony_ci    reportsUnnecessary: undefined,
19207ac75b1Sopenharmony_ci    reportsDeprecated: undefined
19307ac75b1Sopenharmony_ci  };
19407ac75b1Sopenharmony_ci  printDiagnostic(arkTSFAQDiagnostic);
19507ac75b1Sopenharmony_ci}
196