/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const fs = require('fs'); const path = require('path'); const ExcelJS = require('exceljs'); const cm = require('comment-parser'); function requireTypescriptModule() { const buildOption = require('./build.json'); if (buildOption.isBundle) { return require('typescript'); } const tsPathArray = [ path.resolve(__dirname, '../node_modules/typescript'), path.resolve(__dirname, '../../node_modules/typescript') ]; if (fs.existsSync(tsPathArray[0])) { return require(tsPathArray[0]); } else if (fs.existsSync(tsPathArray[1])) { return require(tsPathArray[1]); } return null; } exports.requireTypescriptModule = requireTypescriptModule; const ts = requireTypescriptModule(); const commentNodeWhiteList = [ ts.SyntaxKind.PropertySignature, ts.SyntaxKind.CallSignature, ts.SyntaxKind.MethodSignature, ts.SyntaxKind.MethodDeclaration, ts.SyntaxKind.EnumMember, ts.SyntaxKind.VariableStatement, ts.SyntaxKind.PropertyDeclaration, ts.SyntaxKind.Constructor, ts.SyntaxKind.ModuleDeclaration, ts.SyntaxKind.NamespaceExportDeclaration, ts.SyntaxKind.ClassDeclaration, ts.SyntaxKind.InterfaceDeclaration, ts.SyntaxKind.EnumDeclaration, ts.SyntaxKind.Parameter, ts.SyntaxKind.TypeLiteral, ts.SyntaxKind.FunctionDeclaration, ts.SyntaxKind.LabeledStatement, ts.SyntaxKind.TypeAliasDeclaration ]; exports.commentNodeWhiteList = commentNodeWhiteList; const tagsArrayOfOrder = [ 'namespace', 'struct', 'extends', 'implements', 'typedef', 'interface', 'permission', 'enum', 'constant', 'type', 'param', 'default', 'returns', 'readonly', 'throws', 'static', 'fires', 'syscap', 'systemapi', 'famodelonly', 'FAModelOnly', 'stagemodelonly', 'StageModelOnly', 'crossplatform', 'form', 'atomicservice', 'since', 'deprecated', 'useinstead', 'test', 'form', 'example' ]; exports.tagsArrayOfOrder = tagsArrayOfOrder; const fileTagOrder = ['file', 'kit']; exports.fileTagOrder = fileTagOrder; function getAPINote(node) { const apiLength = node.getText().length; const apiFullLength = node.getFullText().length; return node.getFullText().substring(0, apiFullLength - apiLength); } exports.getAPINote = getAPINote; function hasAPINote(node) { if (!node) { return false; } const apiNote = getAPINote(node).replace(/[\s]/g, ''); if (apiNote && apiNote.length !== 0) { return true; } return false; } exports.hasAPINote = hasAPINote; function removeDir(url) { const statObj = fs.statSync(url); if (statObj.isDirectory()) { let dirs = fs.readdirSync(url); dirs = dirs.map(dir => path.join(url, dir)); for (let i = 0; i < dirs.length; i++) { removeDir(dirs[i]); } fs.rmdirSync(url); } else { fs.unlinkSync(url); } } exports.removeDir = removeDir; function writeResultFile(resultData, outputPath, option) { const STANDARD_INDENT = 2; fs.writeFile(path.resolve(__dirname, outputPath), JSON.stringify(resultData, null, STANDARD_INDENT), option, err => { if (err) { console.error(`ERROR FOR CREATE FILE:${err}`); } else { console.log('API CHECK FINISH!'); } }); } exports.writeResultFile = writeResultFile; function overwriteIndexOf(item, array) { const indexArr = []; for (let i = 0; i < array.length; i++) { if (array[i] === item) { indexArr.push(i); } } return indexArr; } exports.overwriteIndexOf = overwriteIndexOf; const ErrorType = { UNKNOW_DECORATOR: { id: 0, description: 'unknow decorator', }, MISSPELL_WORDS: { id: 1, description: 'misspell words', }, NAMING_ERRORS: { id: 2, description: 'naming errors', }, UNKNOW_PERMISSION: { id: 3, description: 'unknow permission', }, UNKNOW_SYSCAP: { id: 4, description: 'unknow syscap', }, UNKNOW_DEPRECATED: { id: 5, description: 'unknow deprecated', }, WRONG_ORDER: { id: 6, description: 'wrong order', }, WRONG_VALUE: { id: 7, description: 'wrong value', }, WRONG_SCENE: { id: 8, description: 'wrong scene', }, PARAMETER_ERRORS: { id: 9, description: 'wrong parameter', }, API_PAIR_ERRORS: { id: 10, description: 'limited api pair errors', }, ILLEGAL_ANY: { id: 11, description: 'illegal any', }, API_CHANGE_ERRORS: { id: 12, description: 'api change errors', }, }; exports.ErrorType = ErrorType; const LogType = { LOG_API: 'Api', LOG_JSDOC: 'JsDoc', LOG_FILE: 'File', }; exports.LogType = LogType; const ErrorLevel = { HIGH: 3, MIDDLE: 2, LOW: 1, }; exports.ErrorLevel = ErrorLevel; const FileType = { API: 'Api', JSDOC: 'JsDoc', }; exports.FileType = FileType; const apiCheckArr = []; exports.apiCheckArr = apiCheckArr; const apiCheckInfoArr = []; exports.apiCheckInfoArr = apiCheckInfoArr; class ApiCheckResultClass { formatCheckResult = true; } exports.ApiCheckResult = new ApiCheckResultClass(); async function excelApiCheckResult(apiCheckArr) { const workbook = new ExcelJS.Workbook(); const sheet = workbook.addWorksheet('Js Api', { views: [{ xSplit: 1 }] }); sheet.getRow(1).values = ['order', 'errorType', 'fileName', 'apiName', 'apiContent', 'type', 'errorInfo', 'version', 'model']; for (let i = 1; i <= apiCheckArr.length; i++) { const apiData = apiCheckArr[i - 1]; sheet.getRow(i + 1).values = [i, apiData.errorType, apiData.fileName, apiData.apiName, apiData.apiFullText, apiData.type, apiData.errorInfo, apiData.version, apiData.basename]; } const buffer = await workbook.xlsx.writeBuffer(); fs.writeFile('Js_Api.xlsx', buffer, function (err) { if (err) { console.error(err); return; } }); return buffer; } exports.excelApiCheckResult = excelApiCheckResult; function getApiInfo(node) { const notesStr = getAPINote(node); const apiInfo = {}; const versionArr = []; if (notesStr !== '') { if (/\@systemapi/g.test(notesStr)) { apiInfo.isSystemApi = 'system api'; } if (/\@constant/g.test(notesStr)) { apiInfo.isConstant = true; } if (/\@since\s*(\d+)/g.test(notesStr)) { notesStr.replace(/\@since\s*(\d+)/g, (versionInfo) => { versionArr.push(versionInfo); apiInfo.version = versionInfo.replace(/\@since/g, '').trim(); }); apiInfo.humpVersion = versionArr[0].replace(/\@since/g, '').trim(); } if (/\@deprecated.*since\s*(\d+)/g.test(notesStr)) { notesStr.replace(/\@deprecated.*since\s*(\d+)/g, versionInfo => { apiInfo.deprecated = versionInfo.replace( /\@deprecated.*since\s*/g, '').trim(); }); } if (/\@famodelonly/g.test(notesStr)) { notesStr.replace(/\@famodelonly/g, modelInfo => { apiInfo.model = modelInfo; }); } else if (/\@stagemodelonly/g.test(notesStr)) { notesStr.replace(/\@stagemodelonly/g, modelInfo => { apiInfo.model = modelInfo; }); } if (/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) { notesStr.replace(/\@syscap\s*((\w|\.|\/|\{|\@|\}|\s)+)/g, sysCapInfo => { apiInfo.sysCap = sysCapInfo.replace(/\@syscap/g, '').trim(); }); } if (/\@permission\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(notesStr)) { notesStr.replace(/\@permission\s*((\w|\.|\/|\{|\@|\}|\s)+)/g, permissionInfo => { apiInfo.permission = permissionInfo.replace(/\@permission/g, '').trim(); }); } } return apiInfo; } exports.getApiInfo = getApiInfo; function getApiVersion(node) { if (getApiInfo(node).humpVersion) { return getApiInfo(node).humpVersion; } else if (node.parent && !ts.isSourceFile(node.parent)) { return getApiVersion(node.parent); } else { return 'NA'; } } exports.getApiVersion = getApiVersion; function parseJsDoc(node) { if (!hasAPINote(node)) { return []; } else { return cm.parse(getAPINote(node)); } } exports.parseJsDoc = parseJsDoc; function getDeclareValue(declareValue) { let apiDeclareValue = ''; if (!declareValue) { return apiDeclareValue; } if (ts.isFunctionTypeNode(declareValue)) { apiDeclareValue = 'Function'; } else if (ts.isTypeLiteralNode(declareValue)) { apiDeclareValue = 'object'; } else { apiDeclareValue = declareValue.getText().replace(/\n|\r|\s/g, ''); } return apiDeclareValue; } exports.getDeclareValue = getDeclareValue; const systemPermissionFile = path.resolve(__dirname, '../../../../../', 'base/global/system_resources/systemres/main/module.json'); exports.systemPermissionFile = systemPermissionFile; exports.checkOption = { permissionContent: undefined, }; const inheritArr = ['test', 'famodelonly', 'FAModelOnly', 'stagemodelonly', 'StageModelOnly', 'deprecated', 'systemapi']; exports.inheritArr = inheritArr; const ErrorValueInfo = { ERROR_INFO_VALUE_EXTENDS: 'the [extends] tag value is incorrect. Please check if the tag value matches the inherited class name.', ERROR_INFO_VALUE_ENUM: 'the [enum] tag type is incorrect. Please check if the tag type is { string } or { number }', ERROR_INFO_VALUE_SINCE: 'the [since] tag value is incorrect. Please check if the tag value is a numerical value', ERROR_INFO_RETURNS: 'the [returns] tag was used incorrectly. The returns tag should not be used when the return type is void', ERROR_INFO_VALUE_RETURNS: 'the [returns] tag type is incorrect. Please check if the tag type is consistent with the return type', ERROR_INFO_VALUE_USEINSTEAD: 'the [useinstead] tag value is incorrect. Please check the usage method', ERROR_INFO_VALUE_TYPE: 'the [type] tag type is incorrect. Please check if the type matches the attribute type', ERROR_INFO_VALUE_DEFAULT: 'the [default] tag value is incorrect. Please supplement the default value', ERROR_INFO_VALUE_PERMISSION: 'the [permission] tag value is incorrect. Please check if the permission field has been configured or update the configuration file', ERROR_INFO_VALUE_DEPRECATED: 'the [deprecated] tag value is incorrect. Please check the usage method', ERROR_INFO_VALUE_SYSCAP: 'the [syscap] tag value is incorrect. Please check if the syscap field is configured', ERROR_INFO_VALUE_NAMESPACE: 'the [namespace] tag value is incorrect. Please check if it matches the namespace name', ERROR_INFO_VALUE_INTERFACE: 'the [interface] label value is incorrect. Please check if it matches the interface name', ERROR_INFO_VALUE_TYPEDEF: 'the [typedef] tag value is incorrect. Please check if it matches the interface name', ERROR_INFO_TYPE_PARAM: 'the type of the [$$] [param] tag is incorrect. Please check if it matches the type of the [$$] parameter', ERROR_INFO_VALUE_PARAM: 'the value of the [$$] [param] tag is incorrect. Please check if it matches the [$$] parameter name', ERROR_INFO_VALUE1_THROWS: 'the type of the [$$] [throws] tag is incorrect. Please fill in [BusinessError]', ERROR_INFO_VALUE2_THROWS: 'the type of the [$$] [throws] tag is incorrect. Please check if the tag value is a numerical value', ERROR_INFO_INHERIT: 'it was detected that there is an inheritable label [$$] in the current file, but there are child nodes without this label', ERROR_ORDER: 'JSDoc label order error, please make adjustments', ERROR_LABELNAME: 'the [$$] tag does not exist. Please use a valid JSDoc tag', ERROR_LOST_LABEL: 'JSDoc tag validity verification failed. Please confirm if the [$$] tag is missing', ERROR_USE: 'JSDoc label validity verification failed. The [$$] label is not allowed. Please check the label usage method.', ERROR_MORELABEL: 'JSDoc tag validity verification failed. The [$$] [$$] tag is redundant. Please check if the tag should be deleted.', ERROR_REPEATLABEL: 'the validity verification of the JSDoc tag failed. The [$$] tag is not allowed to be reused, please delete the extra tags', ERROR_USE_INTERFACE: 'the validity verification of the JSDoc tag failed. The [interface] tag and [typedef] tag are not allowed to be used simultaneously. Please confirm the interface class.', ERROR_EVENT_NAME_STRING: 'The event name should be string.', ERROR_EVENT_NAME_NULL: 'The event name cannot be Null value.', ERROR_EVENT_NAME_SMALL_HUMP: 'The event name should be named by small hump. (Received [\'$$\'])', ERROR_EVENT_CALLBACK_OPTIONAL: 'The callback parameter of off function should be optional.', ERROR_EVENT_CALLBACK_MISSING: 'The off functions of one single event should have at least one callback parameter, and the callback parameter should be the last parameter.', ERROR_EVENT_ON_AND_OFF_PAIR: 'The on and off event subscription methods do not appear in pair.', ILLEGAL_USE_ANY: 'Illegal [any] keyword used in the API', ERROR_CHANGES_VERSION: 'Please check if the changed API version number is $$.', ERROR_CHANGES_API_HISTORY_PARAM_REQUIRED_CHANGE: 'Forbid changes: Optional parameters cannot be changed to required parameters.', ERROR_CHANGES_API_HISTORY_PARAM_RANGE_CHANGE: 'Forbid changes: Parameters type range cannot be reduced.', ERROR_CHANGES_API_HISTORY_PARAM_WITHOUT_TYPE_CHANGE: 'Forbid changes: Parameters Parameter must be defined by type.', ERROR_CHANGES_API_HISTORY_PARAM_TYPE_CHANGE: 'Forbid changes: Parameters type cannot be modified.', ERROR_CHANGES_API_HISTORY_PARAM_POSITION_CHANGE: 'Forbid changes: Parameters position not be allowed to be modified.', ERROR_CHANGES_API_NEW_REQUIRED_PARAM: 'Forbid changes: Required parameters cannot be created.', ERROR_CHANGES_API_DELETE_PARAM: 'Forbid changes: Parameters cannot be deleted.', ERROR_CHANGES_DEPRECATED: 'Forbid changes: The api has deprecated tag.', ERROR_CHANGES_JSDOC_NUMBER: 'Forbid changes: API changes must add a new section of JSDoc.', ERROR_CHANGES_JSDOC_CHANGE: 'Forbid changes: Previous JSDoc cannot be changed.', ERROR_CHANGES_JSDOC_TRROWS: 'Forbid changes: Throws tag cannot be created.', ERROR_CHANGES_JSDOC_PERMISSION: 'Forbid changes: Permission tag cannot be created or modified.', ERROR_FILE_TAG_ORDER: 'File tags order is incorrect.', ERROR_DESCRIPTION_NULL: 'description is null', ERROR_HAS_CHINESE: 'has chinese' }; exports.ErrorValueInfo = ErrorValueInfo; const DIFF_INFO = { NEW_JSDOCS_LENGTH: 1, NEW_JSDOC_INDEX: 2, }; exports.DIFF_INFO = DIFF_INFO; /** * link error message */ function createErrorInfo(errorInfo, params) { params.forEach((param) => { errorInfo = errorInfo.replace('$$', param); }); return errorInfo; } exports.createErrorInfo = createErrorInfo; /** * judge if it is an API file for Arkui */ function isArkUIApiFile(fileName) { if (fileName.indexOf('component\\ets\\') >= 0 || fileName.indexOf('component/ets/') >= 0) { return true; } return false; } exports.isArkUIApiFile = isArkUIApiFile; function isWhiteListFile(fileName, whiteList) { for (let i = 0; i < whiteList.length; i++) { if (path.normalize(fileName).indexOf(path.normalize(whiteList[i])) !== -1) { return true; } } return false; } exports.isWhiteListFile = isWhiteListFile; function getCheckApiVersion() { const packageJsonPath = path.join(__dirname, '../package.json'); let packageJson; let checkApiVersion; try { const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8'); packageJson = JSON.parse(packageJsonContent); checkApiVersion = packageJson.checkApiVersion; } catch (error) { throw `Failed to read package.json or parse JSON content: ${error}`; } if (!checkApiVersion) { throw 'Please configure the correct API version to be verified'; } return checkApiVersion; } exports.getCheckApiVersion = getCheckApiVersion; const OptionalSymbols = { QUERY: '?', LEFT_BRACKET: '[', RIGHT_BRACKET: ']', LEFT_BRACE: '{', RIGHT_BRACE: '}', LEFT_PARENTHESES: '(', RIGHT_PARENTHESES: ')' }; exports.OptionalSymbols = OptionalSymbols; function removeDuplicateObj(array) { const newArr = []; const errorInfoSet = new Set(); for (const errorInfo of array) { if (!errorInfoSet.has(JSON.stringify(errorInfo))) { errorInfoSet.add(JSON.stringify(errorInfo)); newArr.push(errorInfo); } } return newArr; }; exports.removeDuplicateObj = removeDuplicateObj; // check the api version function checkVersionNeedCheck(node) { const apiVersion = getApiVersion(node); const apiCheckVersion = getCheckApiVersion(); if (parseInt(apiVersion) >= parseInt(apiCheckVersion)) { return true; } return false; } exports.checkVersionNeedCheck = checkVersionNeedCheck; const FUNCTION_TYPES = [ts.SyntaxKind.FunctionDeclaration, ts.SyntaxKind.MethodSignature, ts.SyntaxKind.MethodDeclaration, ts.SyntaxKind.CallSignature, ts.SyntaxKind.Constructor]; exports.FUNCTION_TYPES = FUNCTION_TYPES; function splitPath(filePath, pathElements) { let spliteResult = path.parse(filePath); if (spliteResult.base !== '') { pathElements.add(spliteResult.base); splitPath(spliteResult.dir, pathElements); } } exports.splitPath = splitPath; function isAscending(arr) { for (let i = 1; i < arr.length; i++) { if (arr[i] < arr[i - 1]) { return false; } } return true; } exports.isAscending = isAscending; const ruleArr = ['API_DOC_ATOMICSERVICE_01', 'API_DOC_ATOMICSERVICE_02', 'API_DOC_CONSTANT_01', 'API_DOC_CONSTANT_02', 'API_DOC_CONSTANT_03', 'API_DOC_CONSTANT_04', 'API_DOC_CROSSPLATFORM_01', 'API_DOC_CROSSPLATFORM_02', 'API_DOC_DEFAULT_01', 'API_DOC_DEFAULT_02', 'API_DOC_DEFAULT_04', 'API_DOC_DEPRECATED_01', 'API_DOC_DEPRECATED_02', 'API_DOC_DEPRECATED_03', 'API_DOC_DEPRECATED_04', 'API_DOC_ENUM_01', 'API_DOC_ENUM_02', 'API_DOC_ENUM_03', 'API_DOC_ENUM_04', 'API_DOC_ENUM_05', 'API_DOC_EXAMPLE_01', 'API_DOC_EXAMPLE_02', 'API_DOC_EXTENDS_01', 'API_DOC_EXTENDS_02', 'API_DOC_EXTENDS_03', 'API_DOC_EXTENDS_04', 'API_DOC_EXTENDS_05', 'API_DOC_FAMODELONLY_01', 'API_DOC_FAMODELONLY_02', 'API_DOC_FAMODELONLY_03', 'API_DOC_FIRES_01', 'API_DOC_FIRES_02', 'API_DOC_FORM_01', 'API_DOC_FORM_02', 'API_DOC_IMPLEMENTS_01', 'API_DOC_IMPLEMENTS_04', 'API_DOC_INTERFACE_04', 'API_DOC_INTERFACE_05', 'API_DOC_NAMESPACE_01', 'API_DOC_NAMESPACE_02', 'API_DOC_NAMESPACE_03', 'API_DOC_NAMESPACE_04', 'API_DOC_NAMESPACE_05', 'API_DOC_PARAM_01', 'API_DOC_PARAM_02', 'API_DOC_PARAM_03', 'API_DOC_PARAM_04', 'API_DOC_PARAM_05', 'API_DOC_PARAM_06', 'API_DOC_PERMISSION_02', 'API_DOC_PERMISSION_04', 'API_DOC_READONLY_01', 'API_DOC_READONLY_02', 'API_DOC_READONLY_03', 'API_DOC_RETURNS_01', 'API_DOC_RETURNS_02', 'API_DOC_RETURNS_03', 'API_DOC_RETURNS_04', 'API_DOC_RETURNS_05', 'API_DOC_RETURNS_06', 'API_DOC_SINCE_01', 'API_DOC_SINCE_02', 'API_DOC_SINCE_03', 'API_DOC_SINCE_04', 'API_DOC_STAGEMODELONLY_01', 'API_DOC_STAGEMODELONLY_02', 'API_DOC_STAGEMODELONLY_03', 'API_DOC_STATIC_01', 'API_DOC_STATIC_02', 'API_DOC_STRUCT_02', 'API_DOC_STRUCT_05', 'API_DOC_SYSCAP_02', 'API_DOC_SYSCAP_03', 'API_DOC_SYSCAP_04', 'API_DOC_SYSTEMAPI_01', 'API_DOC_SYSTEMAPI_02', 'API_DOC_SYSTEMAPI_03', 'API_DOC_TEST_01', 'API_DOC_TEST_02', 'API_DOC_TEST_03', 'API_DOC_THROWS_01', 'API_DOC_THROWS_02', 'API_DOC_THROWS_03', 'API_DOC_TYPE_01', 'API_DOC_TYPE_02', 'API_DOC_TYPE_03', 'API_DOC_TYPE_05', 'API_DOC_TYPEDEF_01', 'API_DOC_TYPEDEF_02', 'API_DOC_TYPEDEF_03', 'API_DOC_TYPEDEF_04', 'API_DOC_TYPEDEF_05', 'API_DOC_USEINSTEAD_01', 'API_DOC_USEINSTEAD_02', 'API_DOC_USEINSTEAD_03', 'API_DOC_USEINSTEAD_04', 'API_DOC_UNKNOW_DECORATOR_01', 'API_DEFINE_UNALLOWABLE_01', 'API_DEFINE_NAME_01', 'API_DEFINE_NAME_02', 'API_DEFINE_SPELLING_01', 'API_DEFINE_EVENT_01', 'API_DEFINE_EVENT_02', 'API_DEFINE_EVENT_03', 'API_DEFINE_EVENT_04', 'API_DEFINE_EVENT_05', 'API_DEFINE_EVENT_06', 'API_DEFINE_EVENT_07', 'API_DEFINE_EVENT_08', 'API_DEFINE_HUMP_01', 'API_DEFINE_HUMP_02', 'API_DEFINE_HUMP_03', 'API_DEFINE_HUMP_04', 'API_DEFINE_HUMP_05', 'API_CHANGE_INCOMPATIBLE_01', 'API_CHANGE_INCOMPATIBLE_02', 'API_CHANGE_INCOMPATIBLE_03', 'API_CHANGE_INCOMPATIBLE_04', 'API_CHANGE_INCOMPATIBLE_05', 'API_CHANGE_INCOMPATIBLE_06', 'API_CHANGE_INCOMPATIBLE_07', 'API_CHANGE_INCOMPATIBLE_08', 'API_CHANGE_INCOMPATIBLE_09', 'API_CHANGE_INCOMPATIBLE_10', 'API_CHANGE_INCOMPATIBLE_11', 'API_CHANGE_INCOMPATIBLE_12', 'API_CHANGE_INCOMPATIBLE_13', 'API_CHANGE_INCOMPATIBLE_14', 'API_CHANGE_INCOMPATIBLE_15', 'API_CHANGE_INCOMPATIBLE_16', 'API_CHANGE_INCOMPATIBLE_17', 'API_CHANGE_INCOMPATIBLE_18', 'API_CHANGE_INCOMPATIBLE_19', 'API_CHANGE_INCOMPATIBLE_20', 'API_CHANGE_INCOMPATIBLE_21', 'API_CHANGE_INCOMPATIBLE_22', 'API_CHANGE_INCOMPATIBLE_23', 'API_CHANGE_INCOMPATIBLE_24', 'API_CHANGE_INCOMPATIBLE_25', 'API_CHANGE_INCOMPATIBLE_26', 'API_CHANGE_INCOMPATIBLE_27', 'API_CHANGE_INCOMPATIBLE_28', 'API_CHANGE_INCOMPATIBLE_29', 'API_CHANGE_INCOMPATIBLE_30', 'API_CHANGE_INCOMPATIBLE_31', 'API_CHANGE_INCOMPATIBLE_33', 'API_CHANGE_INCOMPATIBLE_34', 'API_CHANGE_INCOMPATIBLE_35', 'API_CHANGE_INCOMPATIBLE_36', 'API_CHANGE_INCOMPATIBLE_37', 'API_CHANGE_INCOMPATIBLE_38', 'API_CHANGE_INCOMPATIBLE_39', 'API_CHANGE_INCOMPATIBLE_40', 'API_CHANGE_INCOMPATIBLE_41', 'API_DOC_JSDOC_04' ]; exports.ruleArr = ruleArr;