blob: b4bb8b9d77e69a942e3e690c4c174eb50aef6473 [file] [log] [blame]
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var schema = [{
enum: ['always'],
type: 'string'
}, {
additionalProperties: false,
properties: {
annotateUndefined: {
enum: ['always', 'never', 'ignore', 'always-enforce'],
type: 'string'
},
excludeArrowFunctions: {
enum: [false, true, 'expressionsOnly']
},
excludeMatching: {
items: {
type: 'string'
},
type: 'array'
},
includeOnlyMatching: {
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}];
var create = function create(context) {
var annotateReturn = (_lodash2.default.get(context, 'options[0]') || 'always') === 'always';
var annotateUndefined = _lodash2.default.get(context, 'options[1].annotateUndefined') || 'never';
var skipArrows = _lodash2.default.get(context, 'options[1].excludeArrowFunctions') || false;
var makeRegExp = function makeRegExp(str) {
return new RegExp(str);
};
var excludeMatching = _lodash2.default.get(context, 'options[1].excludeMatching', []).map(makeRegExp);
var includeOnlyMatching = _lodash2.default.get(context, 'options[1].includeOnlyMatching', []).map(makeRegExp);
var targetNodes = [];
var registerFunction = function registerFunction(functionNode) {
targetNodes.push({
functionNode
});
};
var isUndefinedReturnType = function isUndefinedReturnType(returnNode) {
return returnNode.argument === null || returnNode.argument.name === 'undefined' || returnNode.argument.operator === 'void';
};
var getIsReturnTypeAnnotationUndefined = function getIsReturnTypeAnnotationUndefined(targetNode) {
var isReturnTypeAnnotationLiteralUndefined = _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.id.name') === 'undefined' && _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.type') === 'GenericTypeAnnotation';
var isReturnTypeAnnotationVoid = _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.type') === 'VoidTypeAnnotation';
var isAsyncReturnTypeAnnotationVoid = _lodash2.default.get(targetNode, 'functionNode.async') && _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.id.name') === 'Promise' && (_lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.typeParameters.params[0].type') === 'VoidTypeAnnotation' || _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.typeParameters.params[0].id.name') === 'undefined' && _lodash2.default.get(targetNode, 'functionNode.returnType.typeAnnotation.typeParameters.params[0].type') === 'GenericTypeAnnotation');
return isReturnTypeAnnotationLiteralUndefined || isReturnTypeAnnotationVoid || isAsyncReturnTypeAnnotationVoid;
};
var shouldFilterNode = function shouldFilterNode(functionNode) {
var isArrow = functionNode.type === 'ArrowFunctionExpression';
var isMethod = functionNode.parent && functionNode.parent.type === 'MethodDefinition';
var propertyNodes = ['Property', 'ClassProperty'];
var isProperty = functionNode.parent && propertyNodes.includes(functionNode.parent.type);
var selector = void 0;
if (isMethod || isProperty) {
selector = 'parent.key.name';
} else if (isArrow) {
selector = 'parent.id.name';
} else {
selector = 'id.name';
}
var identifierName = _lodash2.default.get(functionNode, selector);
var checkRegExp = function checkRegExp(regex) {
return regex.test(identifierName);
};
if (excludeMatching.length && _lodash2.default.some(excludeMatching, checkRegExp)) {
return true;
}
if (includeOnlyMatching.length && !_lodash2.default.some(includeOnlyMatching, checkRegExp)) {
return true;
}
return false;
};
// eslint-disable-next-line complexity
var evaluateFunction = function evaluateFunction(functionNode) {
var targetNode = targetNodes.pop();
if (functionNode !== targetNode.functionNode) {
throw new Error('Mismatch.');
}
var isArrow = functionNode.type === 'ArrowFunctionExpression';
var isArrowFunctionExpression = functionNode.expression;
var isFunctionReturnUndefined = !isArrowFunctionExpression && !functionNode.generator && (!targetNode.returnStatementNode || isUndefinedReturnType(targetNode.returnStatementNode));
var isReturnTypeAnnotationUndefined = getIsReturnTypeAnnotationUndefined(targetNode);
if (skipArrows === 'expressionsOnly' && isArrowFunctionExpression || skipArrows === true && isArrow || shouldFilterNode(functionNode)) {
return;
}
var returnType = functionNode.returnType || isArrow && _lodash2.default.get(functionNode, 'parent.id.typeAnnotation');
if (isFunctionReturnUndefined && isReturnTypeAnnotationUndefined && annotateUndefined === 'never') {
context.report(functionNode, 'Must not annotate undefined return type.');
} else if (isFunctionReturnUndefined && !isReturnTypeAnnotationUndefined && annotateUndefined === 'always') {
context.report(functionNode, 'Must annotate undefined return type.');
} else if ((annotateUndefined === 'always-enforce' || !isFunctionReturnUndefined && !isReturnTypeAnnotationUndefined) && annotateReturn && !returnType && !shouldFilterNode(functionNode)) {
context.report(functionNode, 'Missing return type annotation.');
}
};
var evaluateNoise = function evaluateNoise() {
targetNodes.pop();
};
return {
ArrowFunctionExpression: registerFunction,
'ArrowFunctionExpression:exit': evaluateFunction,
ClassDeclaration: registerFunction,
'ClassDeclaration:exit': evaluateNoise,
ClassExpression: registerFunction,
'ClassExpression:exit': evaluateNoise,
FunctionDeclaration: registerFunction,
'FunctionDeclaration:exit': evaluateFunction,
FunctionExpression: registerFunction,
'FunctionExpression:exit': evaluateFunction,
ReturnStatement: function ReturnStatement(node) {
if (targetNodes.length) {
targetNodes[targetNodes.length - 1].returnStatementNode = node;
}
}
};
};
exports.default = {
create,
schema
};
module.exports = exports.default;