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