* 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 { ErrorType, ErrorLevel, LogType, requireTypescriptModule, checkVersionNeedCheck, createErrorInfo, ErrorValueInfo } = require('./utils');
const { addAPICheckErrorLogs } = require('./compile_info');
const { checkSmallHump } = require('./check_hump');
const ts = requireTypescriptModule();
function checkOnAndOffAppearInPair(node, sourcefile, fileName, onEventAllNames, onEventCheckNames, offEventAllNames, offEventCheckNames) {
for (const value of onEventCheckNames) {
if (!offEventAllNames.has(value)) {
const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_ON_AND_OFF_PAIR, []);
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.API_PAIR_ERRORS, checkErrorResult, LogType.LOG_API,
ErrorLevel.MIDDLE);
}
}
for (const value of offEventCheckNames) {
if (!onEventAllNames.has(value)) {
const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_ON_AND_OFF_PAIR, []);
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.API_PAIR_ERRORS, checkErrorResult, LogType.LOG_API,
ErrorLevel.MIDDLE);
}
}
}
function checkTheFirstParameter(node, sourcefile, fileName) {
if (!checkVersionNeedCheck(node)) {
return;
}
if (node.parameters && node.parameters.length > 0 && node.parameters[0].type) {
const firstParameterType = node.parameters[0].type;
if ((firstParameterType.kind === ts.SyntaxKind.LiteralType && firstParameterType.literal.kind ===
ts.SyntaxKind.StringLiteral)) {
const parameterName = firstParameterType.literal.text;
if (!checkSmallHump(parameterName)) {
const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_SMALL_HUMP, [parameterName]);
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API,
ErrorLevel.MIDDLE);
return;
}
} else if (firstParameterType.kind === ts.SyntaxKind.StringKeyword) {
return;
} else {
let checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_STRING, []);
if (firstParameterType.typeName && firstParameterType.typeName.escapedText === '') {
checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_NULL, []);
}
addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API,
ErrorLevel.MIDDLE);
}
}
}
function isBasicType(node) {
if (node.type !== undefined) {
const nodeKind = node.type.kind;
const basicTypes = new Set([ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.StringKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.UndefinedKeyword,
ts.SyntaxKind.LiteralType]);
if (basicTypes.has(nodeKind)) {
return true;
}
}
return false;
}
function checkOffFunctions(nodes, sourcefile, fileName) {
let isAllCallbackMandatory = true;
let someoneMissingCallback = false;
let someoneHasCallback = false;
for (let node of nodes) {
if (node.parameters.length === 0) {
continue;
}
const lastParameter = node.parameters[node.parameters.length - 1];
if (isBasicType(lastParameter)) {
someoneMissingCallback = true;
} else {
someoneHasCallback = true;
if (lastParameter.questionToken !== undefined) {
isAllCallbackMandatory = false;
}
}
}
if (isAllCallbackMandatory && !someoneMissingCallback) {
const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_CALLBACK_OPTIONAL, []);
addAPICheckErrorLogs(nodes[0], sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API,
ErrorLevel.MIDDLE);
}
}
function extendEventNames(node, allNames, checkNames) {
const nodeType = node.parameters[0].type;
let eventName = '';
if (nodeType.kind === ts.SyntaxKind.LiteralType) {
eventName = nodeType.literal.text;
allNames.add(eventName);
} else if (nodeType.kind === ts.SyntaxKind.StringKeyword) {
eventName = 'string';
allNames.add(eventName);
}
if (checkVersionNeedCheck(node) && eventName !== '') {
checkNames.add(eventName);
}
return eventName;
}
function extendEventNodes(node, eventName, nodesSet) {
if (!nodesSet.get(eventName)) {
nodesSet.set(eventName, [node]);
} else {
let curNodes = nodesSet.get(eventName);
curNodes.push(node);
nodesSet.set(eventName, curNodes);
}
}
function handleVariousEventSubscriptionAPI(childNode, childNodeName, sourcefile, fileName, onEventAllNames, onEventCheckNames,
offEventAllNames, offEventCheckNames, offEventNodes) {
if (childNode.parameters && childNode.parameters.length > 0 && childNode.parameters[0].type) {
if (childNodeName === 'on') {
extendEventNames(childNode, onEventAllNames, onEventCheckNames);
checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName);
} else if (childNodeName === 'off') {
let eventName = extendEventNames(childNode, offEventAllNames, offEventCheckNames);
extendEventNodes(childNode, eventName, offEventNodes);
checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName);
} else if (childNodeName === 'once' || childNodeName === 'emit') {
checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName);
}
}
}
function checkEventSubscription(node, sourcefile, fileName) {
if ((ts.isInterfaceDeclaration(node)) || ts.isModuleBlock(node) || ts.isModuleDeclaration(node) ||
ts.isClassDeclaration(node) || node === sourcefile) {
const onEventAllNames = new Set();
const onEventCheckNames = new Set();
const offEventAllNames = new Set();
const offEventCheckNames = new Set();
const offEventNodes = new Map();
let childNodes = node.members;
if (ts.isModuleDeclaration(node)) {
childNodes = node.body.statements;
}
if (childNodes === undefined) {
return;
}
childNodes.forEach((childNode) => {
if (ts.isFunctionDeclaration(childNode) || ts.isMethodDeclaration(childNode) || ts.isMethodSignature(childNode)) {
let childNodeName = (childNode.name && ts.isIdentifier(childNode.name)) ?
childNode.name.getText() :
'';
handleVariousEventSubscriptionAPI(childNode, childNodeName, sourcefile, fileName, onEventAllNames,
onEventCheckNames, offEventAllNames, offEventCheckNames, offEventNodes);
}
});
for (const event of offEventCheckNames) {
checkOffFunctions(offEventNodes.get(event), sourcefile, fileName);
}
checkOnAndOffAppearInPair(node, sourcefile, fileName, onEventAllNames, onEventCheckNames, offEventAllNames, offEventCheckNames);
}
}
exports.checkEventSubscription = checkEventSubscription;