coffeelint
Version:
Lint your CoffeeScript
88 lines (71 loc) • 2.82 kB
text/coffeescript
BaseLinter = require './base_linter.coffee'
node_children =
Class: ['variable', 'parent', 'body']
Code: ['params', 'body']
For: ['body', 'source', 'guard', 'step']
If: ['condition', 'body', 'elseBody']
Obj: ['properties']
Op: ['first', 'second']
Switch: ['subject', 'cases', 'otherwise']
Try: ['attempt', 'recovery', 'ensure']
Value: ['base', 'properties']
While: ['condition', 'guard', 'body']
hasChildren = (node, children) ->
node?.children?.length is children.length and
node?.children.every (elem, i) -> elem is children[i]
class ASTApi
constructor: (@config) ->
getNodeName: (node) ->
name = node?.constructor?.name
if node_children[name]
return name
else
for own name, children of node_children
if hasChildren(node, children)
return name
# A class that performs static analysis of the abstract
# syntax tree.
module.exports = class ASTLinter extends BaseLinter
constructor : (source, config, rules, @CoffeeScript) ->
super source, config, rules
@astApi = new ASTApi @config
# This uses lintAST instead of lintNode because I think it makes it a bit
# more clear that the rule needs to walk the AST on its own.
acceptRule: (rule) ->
return typeof rule.lintAST is 'function'
lint : () ->
errors = []
try
@node = @CoffeeScript.nodes(@source)
catch coffeeError
# If for some reason you shut off the 'coffeescript_error' rule err
# will be null and should NOT be added to errors
err = @_parseCoffeeScriptError(coffeeError)
errors.push err if err?
return errors
for rule in @rules
@astApi.createError = (attrs = {}) =>
@createError rule.rule.name, attrs
# HACK: Push the local errors object into the plugin. This is a
# temporary solution until I have a way for it to really return
# multiple errors.
rule.errors = errors
v = @normalizeResult rule, rule.lintAST(@node, @astApi)
return v if v?
errors
_parseCoffeeScriptError : (coffeeError) ->
rule = @config['coffeescript_error']
message = coffeeError.toString()
# Parse the line number
lineNumber = -1
if coffeeError.location?
lineNumber = coffeeError.location.first_line + 1
else
match = /line (\d+)/.exec message
lineNumber = parseInt match[1], 10 if match?.length > 1
attrs = {
message: message
level: rule.level
lineNumber: lineNumber
}
return @createError 'coffeescript_error', attrs