| '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; |