UNPKG

@coffeelint/cli

Version:
162 lines (140 loc) 5.05 kB
(function() { var MissingFatArrows, any, containsButIsnt, indexOf = [].indexOf; any = function(arr, test) { return arr.reduce((function(res, elt) { return res || test(elt); }), false); }; containsButIsnt = function(node, nIsThis, nIsClass) { var target; target = void 0; node.traverseChildren(false, function(n) { if (nIsClass(n)) { return false; } if (nIsThis(n)) { target = n; return false; } }); return target; }; module.exports = MissingFatArrows = (function() { class MissingFatArrows { constructor() { this.isCode = this.isCode.bind(this); this.isClass = this.isClass.bind(this); this.isValue = this.isValue.bind(this); this.isObject = this.isObject.bind(this); this.isThis = this.isThis.bind(this); this.isFatArrowCode = this.isFatArrowCode.bind(this); } lintAST(node, astApi) { this.astApi = astApi; this.lintNode(node); return void 0; } lintNode(node, methods = []) { var error, isStrict, ref; isStrict = (ref = this.astApi.config[this.rule.name]) != null ? ref.is_strict : void 0; if (this.isPrototype(node)) { return; } if (this.isConstructor(node)) { return; } // Ignore any nodes we know to be methods if ((!this.isFatArrowCode(node)) && (isStrict ? true : indexOf.call(methods, node) < 0) && (this.needsFatArrow(node))) { error = this.astApi.createError({ lineNumber: node.locationData.first_line + 1, columnNumber: node.locationData.first_column + 1 }); this.errors.push(error); } return node.eachChild((child) => { return this.lintNode(child, (function() { switch (false) { case !this.isClass(node): return this.methodsOfClass(node); // Once we've hit a function, we know we can't be in the top // level of a method anymore, so we can safely reset the methods // to empty to save work. case !this.isCode(node): return []; default: return methods; } }).call(this)); }); } isCode(node) { return this.astApi.getNodeName(node) === 'Code'; } isClass(node) { return this.astApi.getNodeName(node) === 'Class'; } isValue(node) { return this.astApi.getNodeName(node) === 'Value'; } isObject(node) { return this.astApi.getNodeName(node) === 'Obj'; } isPrototype(node) { var i, ident, len, props, ref, ref1; props = (node != null ? (ref = node.variable) != null ? ref.properties : void 0 : void 0) || []; for (i = 0, len = props.length; i < len; i++) { ident = props[i]; if (((ref1 = ident.name) != null ? ref1.value : void 0) === 'prototype') { return true; } } return false; } isThis(node) { return this.isValue(node) && node.base.value === 'this'; } isFatArrowCode(node) { return this.isCode(node) && node.bound; } isConstructor(node) { var ref, ref1; return ((ref = node.variable) != null ? (ref1 = ref.base) != null ? ref1.value : void 0 : void 0) === 'constructor'; } needsFatArrow(node) { return this.isCode(node) && (any(node.params, (param) => { return param.contains(this.isThis) != null; }) || containsButIsnt(node.body, this.isThis, this.isClass)); } methodsOfClass(classNode) { var bodyNodes, returnNode; bodyNodes = classNode.body.expressions; returnNode = bodyNodes[bodyNodes.length - 1]; if ((returnNode != null) && this.isValue(returnNode) && this.isObject(returnNode.base)) { return returnNode.base.properties.map(function(assignNode) { return assignNode.value; }).filter(this.isCode); } else { return []; } } }; MissingFatArrows.prototype.rule = { type: 'problem', name: 'missing_fat_arrows', level: 'ignore', is_strict: false, message: 'Used `this` in a function without a fat arrow', description: `Warns when you use \`this\` inside a function that wasn't defined with a fat arrow. This rule does not apply to methods defined in a class, since they have \`this\` bound to the class instance (or the class itself, for class methods). The option \`is_strict\` is available for checking bindings of class methods. It is impossible to statically determine whether a function using \`this\` will be bound with the correct \`this\` value due to language features like \`Function.prototype.call\` and \`Function.prototype.bind\`, so this rule may produce false positives.` }; return MissingFatArrows; }).call(this); }).call(this);