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 */ 15import fs from 'fs'; 16import path from 'path'; 17import ts from 'typescript'; 18import { Check } from './api_check_plugin'; 19import { AddErrorLogs } from './compile_info'; 20import { 21 ApiResultSimpleInfo, 22 ErrorType, 23 ErrorID, 24 LogType, 25 ErrorLevel, 26 ApiResultInfo, 27 ApiCheckInfo, 28 ErrorBaseInfo, 29} from '../../../typedef/checker/result_type'; 30import { StringConstant } from '../../../utils/Constant'; 31import { CommonFunctions, CompolerOptions, ObtainFullPath } from '../../../utils/checkUtils'; 32import { compositiveResult, compositiveLocalResult } from '../../../utils/checkUtils'; 33 34export class TsSyntaxCheck { 35 /** 36 * ts语法检查工具入口 37 * @param { string } url -File path for storing critical information. 38 */ 39 static checkAPISyntax(url: string): ApiResultSimpleInfo[] { 40 const tsResult: ApiResultSimpleInfo[] = []; 41 const tsLocalResult: ApiResultInfo[] = []; 42 if (fs.existsSync(url)) { 43 const fullFilesPath: string[] = []; 44 ObtainFullPath.getFullFiles(path.resolve(__dirname, '../../../../../../api'), fullFilesPath); 45 const files: Array<string> = Check.getMdFiles(url); 46 const program: ts.Program = ts.createProgram({ 47 rootNames: fullFilesPath, 48 options: CompolerOptions.getCompolerOptions(), 49 }); 50 //ts存在bug,仅为解决无法绑定sourcefile根节点的问题 51 program.getTypeChecker(); 52 const programSourceFiles: readonly ts.SourceFile[] = program.getSourceFiles(); 53 const host: ts.CompilerHost = ts.createCompilerHost(CompolerOptions.getCompolerOptions()); 54 const diagnostics: ts.Diagnostic[] = ts.runArkTSLinter(program, host); 55 files.forEach((filePath: string, index: number) => { 56 TsSyntaxCheck.checkAPISyntaxCallback( 57 filePath, 58 program, 59 diagnostics, 60 programSourceFiles, 61 compositiveResult, 62 compositiveLocalResult 63 ); 64 console.log(`api check scaning file in no ${++index}!`); 65 }); 66 } 67 return tsResult; 68 } 69 /** 70 * ts检查主要处理过程 71 * @param { string } fileName 72 * @param { ts.Program } program 73 * @param { ts.Diagnostic[] } diagnostics 74 * @param { readonly ts.SourceFile[] } programSourceFiles 75 * @param { ApiResultSimpleInfo[] } tsResult 76 * @param { ApiResultInfo[] } checkErrorAllInfos 77 */ 78 static checkAPISyntaxCallback( 79 fileName: string, 80 program: ts.Program, 81 diagnostics: ts.Diagnostic[], 82 programSourceFiles: readonly ts.SourceFile[], 83 tsResult: ApiResultSimpleInfo[], 84 checkErrorAllInfos: ApiResultInfo[] 85 ): void { 86 const fileContent: string = fs.readFileSync(fileName, StringConstant.UTF8); 87 const node: ts.Node = ts.createSourceFile(fileName, fileContent, ts.ScriptTarget.ES2017, true); 88 const fileSuffix: string = fileName.substring(fileName.lastIndexOf('.'), fileName.length); 89 // tsc诊断日志 90 if (fileSuffix === '.ts') { 91 const targetSourceFile: ts.SourceFile = node.getSourceFile(); 92 programSourceFiles.forEach((programSourceFile) => { 93 if (programSourceFile.fileName !== targetSourceFile.fileName) { 94 return; 95 } 96 const result: readonly ts.Diagnostic[] = program.getSemanticDiagnostics(programSourceFile); 97 result.forEach((item) => { 98 const filePath: string = item.file?.fileName as string; 99 const fileName: string = filePath.substring(filePath.indexOf('api'), filePath.length); 100 const apiInfo: ApiCheckInfo = new ApiCheckInfo(); 101 AddErrorLogs.addAPICheckErrorLogs(apiInfo, compositiveResult, compositiveLocalResult); 102 }); 103 }); 104 } 105 // ArkTS诊断日志 106 if (fileSuffix === '.ets') { 107 diagnostics.forEach((item) => { 108 if (path.normalize(item.file?.fileName as string) === path.normalize(fileName)) { 109 const filePath: string = item.file?.fileName as string; 110 const fileName: string = filePath.substring(filePath.indexOf('api'), filePath.length); 111 const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo(); 112 errorBaseInfo 113 .setErrorID(ErrorID.TS_SYNTAX_ERROR_ID) 114 .setErrorLevel(ErrorLevel.MIDDLE) 115 .setErrorType(ErrorType.PARAMETER_ERRORS) 116 .setLogType(LogType.LOG_JSDOC) 117 .setErrorInfo(item.messageText as string); 118 const apiInfoTsCheck: ApiCheckInfo = CommonFunctions.getErrorInfo(undefined, undefined, fileName, 119 errorBaseInfo); 120 AddErrorLogs.addAPICheckErrorLogs(apiInfoTsCheck, compositiveResult, compositiveLocalResult); 121 } 122 }); 123 } 124 } 125} 126