coffeelint
Version:
Lint your CoffeeScript
78 lines (63 loc) • 2.85 kB
text/coffeescript
any = (arr, test) -> arr.reduce ((res, elt) -> res or test elt), false
containsButIsnt = (node, nIsThis, nIsClass) ->
target = undefined
node.traverseChildren false, (n) ->
if nIsClass n
return false
if nIsThis n
target = n
return false
target
module.exports = class MissingFatArrows
rule:
name: 'missing_fat_arrows'
level: 'ignore'
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).
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.
"""
lintAST: (node, ) ->
node
undefined
lintNode: (node, methods = []) ->
if (not node) and
# Ignore any nodes we know to be methods
(node not in methods) and
( node)
error = .createError
lineNumber: node.locationData.first_line + 1
.push error
node.eachChild (child) => child,
switch
when node then 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.
when node then []
else methods
isCode: (node) => .getNodeName(node) is 'Code'
isClass: (node) => .getNodeName(node) is 'Class'
isValue: (node) => .getNodeName(node) is 'Value'
isObject: (node) => .getNodeName(node) is 'Obj'
isThis: (node) => and node.base.value is 'this'
isFatArrowCode: (node) => and node.bound
needsFatArrow: (node) ->
and (
any(node.params, (param) => param.contains()?) or
containsButIsnt(node.body, , )
)
methodsOfClass: (classNode) ->
bodyNodes = classNode.body.expressions
returnNode = bodyNodes[bodyNodes.length - 1]
if returnNode? and and
returnNode.base.properties
.map((assignNode) -> assignNode.value)
.filter()
else []