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 path from 'path'; 17import { AddErrorLogs } from './compile_info'; 18import { StringConstant } from '../../../utils/Constant'; 19import { ApiInfosMap, FileInfoMap } from '../../parser/parser'; 20import { 21 ErrorType, 22 ErrorID, 23 LogType, 24 ErrorLevel, 25 ErrorMessage, 26 ApiCheckInfo, 27 ErrorBaseInfo, 28} from '../../../typedef/checker/result_type'; 29import { Comment } from '../../../typedef/parser/Comment'; 30import { 31 ApiType, 32 BasicApiInfo, 33 ApiInfo, 34 notJsDocApiTypes, 35} from '../../../typedef/parser/ApiInfoDefination'; 36import { CommonFunctions } from '../../../utils/checkUtils'; 37import { compositiveResult, compositiveLocalResult } from '../../../utils/checkUtils'; 38import { currentFilePath } from './api_check_plugin'; 39 40export class CheckHump { 41 /** 42 * 大驼峰检查: 43 * class、interface、枚举名 44 * 45 * @param {string} word 校验名称 46 * @return { boolean } 47 */ 48 static checkLargeHump(word: string): boolean { 49 return /^([A-Z][a-z0-9]*)*$/g.test(word); 50 } 51 /** 52 * 小驼峰检查: 53 * 变量名、方法名、参数名、namespace 54 * 55 * @param {string} word 校验名称 56 * @return { boolean } 57 */ 58 static checkSmallHump(word: string): boolean { 59 return /^[a-z]+[0-9]*([A-Z][a-z0-9]*)*$/g.test(word); 60 } 61 /** 62 * 全大写检查: 63 * 常量命名、枚举值 64 * 65 * @param {string} word 校验名称 66 * @return { boolean } 67 */ 68 static checkAllUppercaseHump(word: string): boolean { 69 return /^[A-Z]+[0-9]*([\_][A-Z0-9]+)*$/g.test(word); 70 } 71 72 /** 73 * 获取fileMap里的对应的apiinfos 74 * 75 * @param { FileInfoMap } fileMap 文件解析结果 76 * @param { string } apiKey 获取api名称 77 * @return {BasicApiInfo[]} apiInfo 78 */ 79 static getApiInfosInFileMap(fileMap: FileInfoMap, apiKey: string): BasicApiInfo[] { 80 if (apiKey === StringConstant.SELF) { 81 return []; 82 } 83 const apiInfoMap: ApiInfosMap = fileMap.get(apiKey) as ApiInfosMap; 84 return apiInfoMap.get(StringConstant.SELF) as BasicApiInfo[]; 85 } 86 87 /** 88 * 遍历所有api,进行api名称校验 89 * 90 * @param {BasicApiInfo[]} apiInfos 91 */ 92 static checkAllAPINameOfHump(apiInfos: BasicApiInfo[]): void { 93 apiInfos.forEach((apiInfo: BasicApiInfo) => { 94 if (!notJsDocApiTypes.has(apiInfo.getApiType())) { 95 CheckHump.checkAPINameOfHump(apiInfo as ApiInfo); 96 } 97 }); 98 } 99 /** 100 * api名称校验 101 * 102 * @param {ApiInfo} apiInfo api节点 103 */ 104 static checkAPINameOfHump(apiInfo: ApiInfo): void { 105 const jsDocInfo: Comment.JsDocInfo | undefined = apiInfo.getLastJsDocInfo(); 106 const publishVersion: string = apiInfo.getJsDocInfos().length > 0 ? apiInfo.getJsDocInfos()[0].getSince() : ''; 107 if (jsDocInfo) { 108 if (jsDocInfo.getDeprecatedVersion() !== '-1') { 109 return; 110 } 111 if (publishVersion !== String(CommonFunctions.getCheckApiVersion())) { 112 return; 113 } 114 } 115 const apiType: string = apiInfo.getApiType(); 116 const filePath: string = apiInfo.getFilePath(); 117 let apiName: string = apiInfo.getApiName(); 118 let checkResult: string = ''; 119 if (apiInfo.getIsJoinType()) { 120 apiName = apiName.split('_')[0]; 121 } 122 if ( 123 apiType === ApiType.ENUM_VALUE || 124 (apiType === ApiType.CONSTANT && filePath.indexOf(`component${path.sep}ets${path.sep}`) === -1) 125 ) { 126 if (!CheckHump.checkAllUppercaseHump(apiName)) { 127 checkResult = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_UPPERCASE_NAME, [apiName]); 128 } 129 } else if ( 130 apiType === ApiType.INTERFACE || 131 apiType === ApiType.CLASS || 132 apiType === ApiType.TYPE_ALIAS || 133 apiType === ApiType.ENUM 134 ) { 135 if (!CheckHump.checkLargeHump(apiName)) { 136 checkResult = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_LARGE_HUMP_NAME, [apiName]); 137 } 138 } else if ( 139 apiType === ApiType.PROPERTY || 140 apiType === ApiType.METHOD || 141 apiType === ApiType.PARAM || 142 apiType === ApiType.NAMESPACE 143 ) { 144 if (!CheckHump.checkSmallHump(apiName)) { 145 checkResult = CommonFunctions.createErrorInfo(ErrorMessage.ERROR_SMALL_HUMP_NAME, [apiName]); 146 } 147 } 148 149 if (checkResult !== '') { 150 const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo(); 151 errorBaseInfo 152 .setErrorID(ErrorID.NAMING_ERRORS_ID) 153 .setErrorLevel(ErrorLevel.MIDDLE) 154 .setErrorType(ErrorType.NAMING_ERRORS) 155 .setLogType(LogType.LOG_JSDOC) 156 .setErrorInfo(checkResult); 157 const apiInfoHump: ApiCheckInfo = CommonFunctions.getErrorInfo(apiInfo, undefined, currentFilePath, 158 errorBaseInfo); 159 AddErrorLogs.addAPICheckErrorLogs(apiInfoHump, compositiveResult, compositiveLocalResult); 160 } 161 } 162 163 /** 164 * 校验文件名称: 165 * 只处理非component/ets/路径下的文件 166 * 传入节点必须是文件的SourceFile节点 167 * 168 * @param {FileInfoMap} fileInfo 文件节点 169 */ 170 static checkAPIFileName(fileInfo: FileInfoMap): void { 171 const fileApiInfo: BasicApiInfo = (fileInfo.get(StringConstant.SELF) as BasicApiInfo[])[0]; 172 if (fileApiInfo.getApiType() !== ApiType.SOURCE_FILE) { 173 return; 174 } 175 const filePath: string = fileApiInfo.getFilePath(); 176 if (filePath.indexOf(`component${path.sep}ets${path.sep}`) !== -1) { 177 return; 178 } 179 let moduleName = ''; 180 let exportAssignment = ''; 181 let version = 'NA'; 182 for (const apiKey of fileInfo.keys()) { 183 const apiInfos: BasicApiInfo[] = CheckHump.getApiInfosInFileMap(fileInfo, apiKey); 184 apiInfos.forEach((apiInfo) => { 185 if (!notJsDocApiTypes.has(apiInfo.getApiType())) { 186 const jsDocInfos: Comment.JsDocInfo[] = (apiInfo as ApiInfo).getJsDocInfos(); 187 version = jsDocInfos[0] ? CommonFunctions.getSinceVersion(jsDocInfos[0].getSince()) : version; 188 } 189 moduleName = apiInfo.getApiType() === ApiType.NAMESPACE ? apiInfo.getApiName() : moduleName; 190 exportAssignment = (apiInfo.getApiType() === ApiType.EXPORT_DEFAULT || apiInfo.getIsExport()) ? apiInfo.getApiName().replace(StringConstant.EXPORT_DEFAULT, '') : exportAssignment; 191 }); 192 } 193 const basename: string = path.basename(filePath).replace(new RegExp(StringConstant.DTS_EXTENSION, 'g'), '').replace(new RegExp(StringConstant.DETS_EXTENSION, 'g'), ''); 194 195 const basenames: string[] = basename.split('.'); 196 const lastModuleName: string = basenames.length ? basenames[basenames.length - 1] : ''; 197 let checkResult: string = ''; 198 199 if (moduleName !== '' && exportAssignment === moduleName && !CheckHump.checkSmallHump(lastModuleName)) { 200 checkResult = ErrorMessage.ERROR_SMALL_HUMP_NAME_FILE; 201 } else if (moduleName === '' && exportAssignment !== moduleName && !CheckHump.checkLargeHump(lastModuleName)) { 202 checkResult = ErrorMessage.ERROR_LARGE_HUMP_NAME_FILE; 203 } 204 if (checkResult !== '' && version === String(CommonFunctions.getCheckApiVersion())) { 205 const errorBaseInfo: ErrorBaseInfo = new ErrorBaseInfo(); 206 errorBaseInfo 207 .setErrorID(ErrorID.NAMING_ERRORS_ID) 208 .setErrorLevel(ErrorLevel.MIDDLE) 209 .setErrorType(ErrorType.NAMING_ERRORS) 210 .setLogType(LogType.LOG_JSDOC) 211 .setErrorInfo(checkResult); 212 const apiInfoHump: ApiCheckInfo = CommonFunctions.getErrorInfo(fileApiInfo, undefined, currentFilePath, 213 errorBaseInfo); 214 AddErrorLogs.addAPICheckErrorLogs(apiInfoHump, compositiveResult, compositiveLocalResult); 215 } 216 } 217} 218