UNPKG

coffeelint

Version:
1,559 lines (1,330 loc) 107 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.coffeelint = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ module.exports={ "name": "coffeelint", "description": "Lint your CoffeeScript", "version": "2.1.0", "homepage": "http://www.coffeelint.org", "keywords": [ "lint", "coffeescript", "coffee-script" ], "author": "Matthew Perpick <clutchski@gmail.com>", "main": "./lib/coffeelint.js", "engines": { "npm": ">=1.3.7", "node": ">=6.9.1" }, "repository": { "type": "git", "url": "git://github.com/clutchski/coffeelint.git" }, "bin": { "coffeelint": "./bin/coffeelint" }, "dependencies": { "coffeescript": "^2.1.0", "glob": "^7.0.6", "ignore": "^3.0.9", "optimist": "^0.6.1", "resolve": "^0.6.3", "strip-json-comments": "^1.0.2" }, "devDependencies": { "vows": ">=0.8.1", "underscore": ">=1.4.4" }, "license": "MIT", "scripts": { "pretest": "cake compile", "test": "./vowsrunner.js --spec test/*.coffee test/*.litcoffee", "testrule": "npm run compile && ./vowsrunner.js --spec", "posttest": "npm run lint", "prepublish": "cake prepublish", "postpublish": "cake postpublish", "publish": "cake publish", "lint": "cake compile && ./bin/coffeelint .", "lint-csv": "cake compile && ./bin/coffeelint --csv .", "lint-jslint": "cake compile && ./bin/coffeelint --jslint .", "compile": "cake compile" } } },{}],2:[function(require,module,exports){ var ASTApi, ASTLinter, BaseLinter, hasChildren, node_children, hasProp = {}.hasOwnProperty, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 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 = function(node, children) { var ref; return (node != null ? (ref = node.children) != null ? ref.length : void 0 : void 0) === children.length && (node != null ? node.children.every(function(elem, i) { return elem === children[i]; }) : void 0); }; ASTApi = (function() { function ASTApi(config1) { this.config = config1; } ASTApi.prototype.getNodeName = function(node) { var children, name, ref; name = node != null ? (ref = node.constructor) != null ? ref.name : void 0 : void 0; if (node_children[name]) { return name; } else { for (name in node_children) { if (!hasProp.call(node_children, name)) continue; children = node_children[name]; if (hasChildren(node, children)) { return name; } } } }; return ASTApi; })(); module.exports = ASTLinter = (function(superClass) { extend(ASTLinter, superClass); function ASTLinter(source, config, rules, CoffeeScript) { this.CoffeeScript = CoffeeScript; ASTLinter.__super__.constructor.call(this, source, config, rules); this.astApi = new ASTApi(this.config); } ASTLinter.prototype.acceptRule = function(rule) { return typeof rule.lintAST === 'function'; }; ASTLinter.prototype.lint = function() { var coffeeError, err, errors, j, len, ref, rule, v; errors = []; try { this.node = this.CoffeeScript.nodes(this.source); } catch (error) { coffeeError = error; err = this._parseCoffeeScriptError(coffeeError); if (err != null) { errors.push(err); } return errors; } ref = this.rules; for (j = 0, len = ref.length; j < len; j++) { rule = ref[j]; this.astApi.createError = (function(_this) { return function(attrs) { if (attrs == null) { attrs = {}; } return _this.createError(rule.rule.name, attrs); }; })(this); rule.errors = errors; v = this.normalizeResult(rule, rule.lintAST(this.node, this.astApi)); if (v != null) { return v; } } return errors; }; ASTLinter.prototype._parseCoffeeScriptError = function(coffeeError) { var attrs, lineNumber, match, message, rule; rule = this.config['coffeescript_error']; message = coffeeError.toString(); lineNumber = -1; if (coffeeError.location != null) { lineNumber = coffeeError.location.first_line + 1; } else { match = /line (\d+)/.exec(message); if ((match != null ? match.length : void 0) > 1) { lineNumber = parseInt(match[1], 10); } } attrs = { message: message, level: rule.level, lineNumber: lineNumber }; return this.createError('coffeescript_error', attrs); }; return ASTLinter; })(BaseLinter); },{"./base_linter.coffee":3}],3:[function(require,module,exports){ var BaseLinter, defaults, extend, slice = [].slice; extend = function() { var destination, i, k, len, source, sources, v; destination = arguments[0], sources = 2 <= arguments.length ? slice.call(arguments, 1) : []; for (i = 0, len = sources.length; i < len; i++) { source = sources[i]; for (k in source) { v = source[k]; destination[k] = v; } } return destination; }; defaults = function(source, defaults) { return extend({}, defaults, source); }; module.exports = BaseLinter = (function() { function BaseLinter(source1, config, rules) { this.source = source1; this.config = config; this.setupRules(rules); } BaseLinter.prototype.isObject = function(obj) { return obj === Object(obj); }; BaseLinter.prototype.createError = function(ruleName, attrs) { var level; if (attrs == null) { attrs = {}; } if (attrs.level == null) { attrs.level = this.config[ruleName].level; } level = attrs.level; if (level !== 'ignore' && level !== 'warn' && level !== 'error') { throw new Error("unknown level " + level + " for rule: " + ruleName); } if (level === 'error' || level === 'warn') { attrs.rule = ruleName; return defaults(attrs, this.config[ruleName]); } else { return null; } }; BaseLinter.prototype.acceptRule = function(rule) { throw new Error('acceptRule needs to be overridden in the subclass'); }; BaseLinter.prototype.setupRules = function(rules) { var RuleConstructor, level, name, results, rule; this.rules = []; results = []; for (name in rules) { RuleConstructor = rules[name]; level = this.config[name].level; if (level === 'error' || level === 'warn') { rule = new RuleConstructor(this, this.config); if (this.acceptRule(rule)) { results.push(this.rules.push(rule)); } else { results.push(void 0); } } else if (level !== 'ignore') { throw new Error("unknown level " + level + " for rule: " + rule); } else { results.push(void 0); } } return results; }; BaseLinter.prototype.normalizeResult = function(p, result) { if (result === true) { return this.createError(p.rule.name); } if (this.isObject(result)) { return this.createError(p.rule.name, result); } }; return BaseLinter; })(); },{}],4:[function(require,module,exports){ /* CoffeeLint Copyright (c) 2011 Matthew Perpick. CoffeeLint is freely distributable under the MIT license. */ var ASTLinter, CoffeeScript, ERROR, ErrorReport, IGNORE, LexicalLinter, LineLinter, RULES, WARN, _rules, cache, coffeelint, defaults, difference, extend, hasSyntaxError, mergeDefaultConfig, nodeRequire, packageJSON, sameJSON, union, slice = [].slice, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; coffeelint = exports; nodeRequire = require; if (typeof window !== "undefined" && window !== null) { CoffeeScript = window.CoffeeScript; } if (CoffeeScript == null) { CoffeeScript = nodeRequire('coffeescript'); } if (CoffeeScript == null) { throw new Error('Unable to find CoffeeScript'); } packageJSON = require('./../package.json'); coffeelint.VERSION = packageJSON.version; ERROR = 'error'; WARN = 'warn'; IGNORE = 'ignore'; coffeelint.RULES = RULES = require('./rules.coffee'); extend = function() { var destination, j, k, len, source, sources, v; destination = arguments[0], sources = 2 <= arguments.length ? slice.call(arguments, 1) : []; for (j = 0, len = sources.length; j < len; j++) { source = sources[j]; for (k in source) { v = source[k]; destination[k] = v; } } return destination; }; defaults = function(source, defaults) { return extend({}, defaults, source); }; union = function(a, b) { var c, j, len, len1, n, results, x; c = {}; for (j = 0, len = a.length; j < len; j++) { x = a[j]; c[x] = true; } for (n = 0, len1 = b.length; n < len1; n++) { x = b[n]; c[x] = true; } results = []; for (x in c) { results.push(x); } return results; }; difference = function(a, b) { var j, len, results, x; results = []; for (j = 0, len = a.length; j < len; j++) { x = a[j]; if (indexOf.call(b, x) < 0) { results.push(x); } } return results; }; LineLinter = require('./line_linter.coffee'); LexicalLinter = require('./lexical_linter.coffee'); ASTLinter = require('./ast_linter.coffee'); cache = null; mergeDefaultConfig = function(userConfig) { var config, rule, ruleConfig, ruleLoader; try { ruleLoader = nodeRequire('./ruleLoader'); ruleLoader.loadFromConfig(coffeelint, userConfig); } catch (error) {} config = {}; if (userConfig.coffeelint) { config.coffeelint = userConfig.coffeelint; } for (rule in RULES) { ruleConfig = RULES[rule]; config[rule] = defaults(userConfig[rule], ruleConfig); } return config; }; sameJSON = function(a, b) { return JSON.stringify(a) === JSON.stringify(b); }; coffeelint.trimConfig = function(userConfig) { var config, dConfig, dValue, key, newConfig, ref, rule, value; newConfig = {}; userConfig = mergeDefaultConfig(userConfig); for (rule in userConfig) { config = userConfig[rule]; dConfig = RULES[rule]; if (rule === 'coffeelint') { config.transforms = config._transforms; delete config._transforms; config.coffeescript = config._coffeescript; delete config._coffeescript; newConfig[rule] = config; } else if ((config.level === (ref = dConfig.level) && ref === 'ignore')) { void 0; } else if (config.level === 'ignore') { newConfig[rule] = { level: 'ignore' }; } else { config.module = config._module; delete config._module; for (key in config) { value = config[key]; if (key === 'message' || key === 'description' || key === 'name') { continue; } dValue = dConfig[key]; if (value !== dValue && !sameJSON(value, dValue)) { if (newConfig[rule] == null) { newConfig[rule] = {}; } newConfig[rule][key] = value; } } } } return newConfig; }; coffeelint.invertLiterate = function(source) { var j, len, line, newSource, ref; source = CoffeeScript.helpers.invertLiterate(source); newSource = ''; ref = source.split('\n'); for (j = 0, len = ref.length; j < len; j++) { line = ref[j]; if (line.match(/^#/)) { line = line.replace(/\s*$/, ''); } line = line.replace(/^[ ]{4}|^\t/g, ''); newSource += line + "\n"; } return newSource; }; _rules = {}; coffeelint.registerRule = function(RuleConstructor, ruleName) { var e, name, p, ref, ref1; if (ruleName == null) { ruleName = void 0; } p = new RuleConstructor; name = (p != null ? (ref = p.rule) != null ? ref.name : void 0 : void 0) || '(unknown)'; e = function(msg) { throw new Error("Invalid rule: " + name + " " + msg); }; if (p.rule == null) { e('Rules must provide rule attribute with a default configuration.'); } if (p.rule.name == null) { e('Rule defaults require a name'); } if ((ruleName != null) && ruleName !== p.rule.name) { e("Mismatched rule name: " + ruleName); } if (p.rule.message == null) { e('Rule defaults require a message'); } if (p.rule.description == null) { e('Rule defaults require a description'); } if ((ref1 = p.rule.level) !== 'ignore' && ref1 !== 'warn' && ref1 !== 'error') { e("Default level must be 'ignore', 'warn', or 'error'"); } if (typeof p.lintToken === 'function') { if (!p.tokens) { e("'tokens' is required for 'lintToken'"); } } else if (typeof p.lintLine !== 'function' && typeof p.lintAST !== 'function') { e('Rules must implement lintToken, lintLine, or lintAST'); } RULES[p.rule.name] = p.rule; return _rules[p.rule.name] = RuleConstructor; }; coffeelint.getRules = function() { var j, key, len, output, ref; output = {}; ref = Object.keys(RULES).sort(); for (j = 0, len = ref.length; j < len; j++) { key = ref[j]; output[key] = RULES[key]; } return output; }; coffeelint.registerRule(require('./rules/arrow_spacing.coffee')); coffeelint.registerRule(require('./rules/braces_spacing.coffee')); coffeelint.registerRule(require('./rules/no_tabs.coffee')); coffeelint.registerRule(require('./rules/no_trailing_whitespace.coffee')); coffeelint.registerRule(require('./rules/max_line_length.coffee')); coffeelint.registerRule(require('./rules/line_endings.coffee')); coffeelint.registerRule(require('./rules/no_trailing_semicolons.coffee')); coffeelint.registerRule(require('./rules/indentation.coffee')); coffeelint.registerRule(require('./rules/camel_case_classes.coffee')); coffeelint.registerRule(require('./rules/colon_assignment_spacing.coffee')); coffeelint.registerRule(require('./rules/no_implicit_braces.coffee')); coffeelint.registerRule(require('./rules/no_nested_string_interpolation.coffee')); coffeelint.registerRule(require('./rules/no_plusplus.coffee')); coffeelint.registerRule(require('./rules/no_throwing_strings.coffee')); coffeelint.registerRule(require('./rules/no_backticks.coffee')); coffeelint.registerRule(require('./rules/no_implicit_parens.coffee')); coffeelint.registerRule(require('./rules/no_empty_param_list.coffee')); coffeelint.registerRule(require('./rules/no_stand_alone_at.coffee')); coffeelint.registerRule(require('./rules/space_operators.coffee')); coffeelint.registerRule(require('./rules/duplicate_key.coffee')); coffeelint.registerRule(require('./rules/empty_constructor_needs_parens.coffee')); coffeelint.registerRule(require('./rules/cyclomatic_complexity.coffee')); coffeelint.registerRule(require('./rules/newlines_after_classes.coffee')); coffeelint.registerRule(require('./rules/no_unnecessary_fat_arrows.coffee')); coffeelint.registerRule(require('./rules/missing_fat_arrows.coffee')); coffeelint.registerRule(require('./rules/non_empty_constructor_needs_parens.coffee')); coffeelint.registerRule(require('./rules/no_unnecessary_double_quotes.coffee')); coffeelint.registerRule(require('./rules/no_debugger.coffee')); coffeelint.registerRule(require('./rules/no_interpolation_in_single_quotes.coffee')); coffeelint.registerRule(require('./rules/no_empty_functions.coffee')); coffeelint.registerRule(require('./rules/prefer_english_operator.coffee')); coffeelint.registerRule(require('./rules/spacing_after_comma.coffee')); coffeelint.registerRule(require('./rules/transform_messes_up_line_numbers.coffee')); coffeelint.registerRule(require('./rules/ensure_comprehensions.coffee')); coffeelint.registerRule(require('./rules/no_this.coffee')); coffeelint.registerRule(require('./rules/eol_last.coffee')); coffeelint.registerRule(require('./rules/no_private_function_fat_arrows.coffee')); hasSyntaxError = function(source) { try { CoffeeScript.tokens(source); return false; } catch (error) {} return true; }; ErrorReport = require('./error_report.coffee'); coffeelint.getErrorReport = function() { return new ErrorReport(coffeelint); }; coffeelint.lint = function(source, userConfig, literate) { var allErrors, astErrors, cmd, config, disabled, disabledEntirely, disabledInitially, disabledLine, e, errors, i, inlineConfig, j, l, len, len1, lexErrors, lexicalLinter, lineErrors, lineLinter, m, n, name, nextLine, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, regex, rule, ruleLoader, rules, set, sourceLength, tokensByLine, transform; if (userConfig == null) { userConfig = {}; } if (literate == null) { literate = false; } errors = []; if (cache != null) { cache.setConfig(userConfig); } if (cache != null ? cache.has(source) : void 0) { return cache != null ? cache.get(source) : void 0; } config = mergeDefaultConfig(userConfig); if (literate) { source = this.invertLiterate(source); } if ((userConfig != null ? (ref = userConfig.coffeelint) != null ? ref.transforms : void 0 : void 0) != null) { sourceLength = source.split('\n').length; ref2 = userConfig != null ? (ref1 = userConfig.coffeelint) != null ? ref1.transforms : void 0 : void 0; for (j = 0, len = ref2.length; j < len; j++) { m = ref2[j]; try { ruleLoader = nodeRequire('./ruleLoader'); transform = ruleLoader.require(m); source = transform(source); } catch (error) {} } if (sourceLength !== source.split('\n').length && config.transform_messes_up_line_numbers.level !== 'ignore') { errors.push(extend({ lineNumber: 1, context: "File was transformed from " + sourceLength + " lines to " + (source.split("\n").length) + " lines" }, config.transform_messes_up_line_numbers)); } } if ((userConfig != null ? (ref3 = userConfig.coffeelint) != null ? ref3.coffeescript : void 0 : void 0) != null) { CoffeeScript = ruleLoader.require(userConfig.coffeelint.coffeescript); } for (name in userConfig) { if (name !== 'coffeescript_error' && name !== '_comment') { if (_rules[name] == null) { void 0; } } } disabledInitially = []; ref4 = source.split('\n'); for (n = 0, len1 = ref4.length; n < len1; n++) { l = ref4[n]; ref5 = LineLinter.getDirective(l) || [], regex = ref5[0], set = ref5[1], rule = ref5[ref5.length - 1]; if ((set === 'enable' || set === 'enable-line') && ((ref6 = config[rule]) != null ? ref6.level : void 0) === 'ignore') { disabledInitially.push(rule); config[rule].level = 'error'; } } astErrors = new ASTLinter(source, config, _rules, CoffeeScript).lint(); errors = errors.concat(astErrors); if (!hasSyntaxError(source)) { lexicalLinter = new LexicalLinter(source, config, _rules, CoffeeScript); lexErrors = lexicalLinter.lint(); errors = errors.concat(lexErrors); tokensByLine = lexicalLinter.tokensByLine; lineLinter = new LineLinter(source, config, _rules, tokensByLine, literate); lineErrors = lineLinter.lint(); errors = errors.concat(lineErrors); inlineConfig = lineLinter.inlineConfig; } else { inlineConfig = { enable: {}, disable: {}, 'enable-line': {}, 'disable-line': {} }; } errors.sort(function(a, b) { return a.lineNumber - b.lineNumber; }); disabledEntirely = (function() { var len2, map, o, ref7, result; result = []; map = {}; ref7 = errors || []; for (o = 0, len2 = ref7.length; o < len2; o++) { name = ref7[o].name; if (!map[name]) { result.push(name); map[name] = true; } } return result; })(); allErrors = errors; errors = []; disabled = disabledInitially; nextLine = 0; for (i = o = 0, ref7 = source.split('\n').length; 0 <= ref7 ? o < ref7 : o > ref7; i = 0 <= ref7 ? ++o : --o) { disabledLine = disabled; for (cmd in inlineConfig) { rules = inlineConfig[cmd][i]; if (rules != null) { ({ 'disable': function() { if (rules.length) { disabled = union(disabled, rules); return disabledLine = union(disabledLine, rules); } else { return disabled = disabledLine = disabledEntirely; } }, 'disable-line': function() { if (rules.length) { return disabledLine = union(disabledLine, rules); } else { return disabledLine = disabledEntirely; } }, 'enable': function() { if (rules.length) { disabled = difference(disabled, rules); return disabledLine = difference(disabledLine, rules); } else { return disabled = disabledLine = disabledInitially; } }, 'enable-line': function() { if (rules.length) { return disabledLine = difference(disabledLine, rules); } else { return disabledLine = disabledInitially; } } })[cmd](); } } while (nextLine === i && allErrors.length > 0) { nextLine = allErrors[0].lineNumber - 1; e = allErrors[0]; if (e.lineNumber === i + 1 || (e.lineNumber == null)) { e = allErrors.shift(); if (ref8 = e.rule, indexOf.call(disabledLine, ref8) < 0) { errors.push(e); } } } } if (cache != null) { cache.set(source, errors); } return errors; }; coffeelint.setCache = function(obj) { return cache = obj; }; },{"./../package.json":1,"./ast_linter.coffee":2,"./error_report.coffee":5,"./lexical_linter.coffee":6,"./line_linter.coffee":7,"./rules.coffee":8,"./rules/arrow_spacing.coffee":9,"./rules/braces_spacing.coffee":10,"./rules/camel_case_classes.coffee":11,"./rules/colon_assignment_spacing.coffee":12,"./rules/cyclomatic_complexity.coffee":13,"./rules/duplicate_key.coffee":14,"./rules/empty_constructor_needs_parens.coffee":15,"./rules/ensure_comprehensions.coffee":16,"./rules/eol_last.coffee":17,"./rules/indentation.coffee":18,"./rules/line_endings.coffee":19,"./rules/max_line_length.coffee":20,"./rules/missing_fat_arrows.coffee":21,"./rules/newlines_after_classes.coffee":22,"./rules/no_backticks.coffee":23,"./rules/no_debugger.coffee":24,"./rules/no_empty_functions.coffee":25,"./rules/no_empty_param_list.coffee":26,"./rules/no_implicit_braces.coffee":27,"./rules/no_implicit_parens.coffee":28,"./rules/no_interpolation_in_single_quotes.coffee":29,"./rules/no_nested_string_interpolation.coffee":30,"./rules/no_plusplus.coffee":31,"./rules/no_private_function_fat_arrows.coffee":32,"./rules/no_stand_alone_at.coffee":33,"./rules/no_tabs.coffee":34,"./rules/no_this.coffee":35,"./rules/no_throwing_strings.coffee":36,"./rules/no_trailing_semicolons.coffee":37,"./rules/no_trailing_whitespace.coffee":38,"./rules/no_unnecessary_double_quotes.coffee":39,"./rules/no_unnecessary_fat_arrows.coffee":40,"./rules/non_empty_constructor_needs_parens.coffee":41,"./rules/prefer_english_operator.coffee":42,"./rules/space_operators.coffee":43,"./rules/spacing_after_comma.coffee":44,"./rules/transform_messes_up_line_numbers.coffee":45}],5:[function(require,module,exports){ var ErrorReport; module.exports = ErrorReport = (function() { function ErrorReport(coffeelint) { this.coffeelint = coffeelint; this.paths = {}; } ErrorReport.prototype.lint = function(filename, source, config, literate) { if (config == null) { config = {}; } if (literate == null) { literate = false; } return this.paths[filename] = this.coffeelint.lint(source, config, literate); }; ErrorReport.prototype.getExitCode = function() { var path; for (path in this.paths) { if (this.pathHasError(path)) { return 1; } } return 0; }; ErrorReport.prototype.getSummary = function() { var error, errorCount, errors, i, len, path, pathCount, ref, warningCount; pathCount = errorCount = warningCount = 0; ref = this.paths; for (path in ref) { errors = ref[path]; pathCount++; for (i = 0, len = errors.length; i < len; i++) { error = errors[i]; if (error.level === 'error') { errorCount++; } if (error.level === 'warn') { warningCount++; } } } return { errorCount: errorCount, warningCount: warningCount, pathCount: pathCount }; }; ErrorReport.prototype.getErrors = function(path) { return this.paths[path]; }; ErrorReport.prototype.pathHasWarning = function(path) { return this._hasLevel(path, 'warn'); }; ErrorReport.prototype.pathHasError = function(path) { return this._hasLevel(path, 'error'); }; ErrorReport.prototype.hasError = function() { var path; for (path in this.paths) { if (this.pathHasError(path)) { return true; } } return false; }; ErrorReport.prototype._hasLevel = function(path, level) { var error, i, len, ref; ref = this.paths[path]; for (i = 0, len = ref.length; i < len; i++) { error = ref[i]; if (error.level === level) { return true; } } return false; }; return ErrorReport; })(); },{}],6:[function(require,module,exports){ var BaseLinter, LexicalLinter, TokenApi, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; TokenApi = (function() { function TokenApi(CoffeeScript, source, config1, tokensByLine) { this.config = config1; this.tokensByLine = tokensByLine; this.tokens = CoffeeScript.tokens(source); this.lines = source.split('\n'); this.tokensByLine = {}; } TokenApi.prototype.i = 0; TokenApi.prototype.peek = function(n) { if (n == null) { n = 1; } return this.tokens[this.i + n] || null; }; return TokenApi; })(); BaseLinter = require('./base_linter.coffee'); module.exports = LexicalLinter = (function(superClass) { extend(LexicalLinter, superClass); function LexicalLinter(source, config, rules, CoffeeScript) { LexicalLinter.__super__.constructor.call(this, source, config, rules); this.tokenApi = new TokenApi(CoffeeScript, source, this.config, this.tokensByLine); this.tokensByLine = this.tokenApi.tokensByLine; } LexicalLinter.prototype.acceptRule = function(rule) { return typeof rule.lintToken === 'function'; }; LexicalLinter.prototype.lint = function() { var error, errors, i, j, k, len, len1, ref, ref1, token; errors = []; ref = this.tokenApi.tokens; for (i = j = 0, len = ref.length; j < len; i = ++j) { token = ref[i]; this.tokenApi.i = i; ref1 = this.lintToken(token); for (k = 0, len1 = ref1.length; k < len1; k++) { error = ref1[k]; errors.push(error); } } return errors; }; LexicalLinter.prototype.lintToken = function(token) { var base, errors, j, len, lineNumber, ref, ref1, ref2, rule, type, v, value; type = token[0], value = token[1], (ref = token[2], lineNumber = ref.first_line); if ((base = this.tokensByLine)[lineNumber] == null) { base[lineNumber] = []; } this.tokensByLine[lineNumber].push(token); this.lineNumber = lineNumber || this.lineNumber || 0; this.tokenApi.lineNumber = this.lineNumber; errors = []; ref1 = this.rules; for (j = 0, len = ref1.length; j < len; j++) { rule = ref1[j]; if (!(ref2 = token[0], indexOf.call(rule.tokens, ref2) >= 0)) { continue; } v = this.normalizeResult(rule, rule.lintToken(token, this.tokenApi)); if (v != null) { errors.push(v); } } return errors; }; LexicalLinter.prototype.createError = function(ruleName, attrs) { if (attrs == null) { attrs = {}; } if (attrs.lineNumber == null) { attrs.lineNumber = this.lineNumber; } attrs.lineNumber += 1; attrs.line = this.tokenApi.lines[attrs.lineNumber - 1]; return LexicalLinter.__super__.createError.call(this, ruleName, attrs); }; return LexicalLinter; })(BaseLinter); },{"./base_linter.coffee":3}],7:[function(require,module,exports){ var BaseLinter, LineApi, LineLinter, configShortcuts, configStatement, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; LineApi = (function() { function LineApi(source, config1, tokensByLine1, literate1) { this.config = config1; this.tokensByLine = tokensByLine1; this.literate = literate1; this.line = null; this.lines = source.split('\n'); this.lineCount = this.lines.length; this.context = { "class": { inClass: false, lastUnemptyLineInClass: null, classIndents: null } }; } LineApi.prototype.lineNumber = 0; LineApi.prototype.isLiterate = function() { return this.literate; }; LineApi.prototype.maintainClassContext = function(line) { if (this.context["class"].inClass) { if (this.lineHasToken('INDENT')) { this.context["class"].classIndents++; } else if (this.lineHasToken('OUTDENT')) { this.context["class"].classIndents--; if (this.context["class"].classIndents === 0) { this.context["class"].inClass = false; this.context["class"].classIndents = null; } } if (!line.match(/^\s*$/)) { this.context["class"].lastUnemptyLineInClass = this.lineNumber; } } else { if (!line.match(/\\s*/)) { this.context["class"].lastUnemptyLineInClass = null; } if (this.lineHasToken('CLASS')) { this.context["class"].inClass = true; this.context["class"].lastUnemptyLineInClass = this.lineNumber; this.context["class"].classIndents = 0; } } return null; }; LineApi.prototype.isLastLine = function() { return this.lineNumber === this.lineCount - 1; }; LineApi.prototype.lineHasToken = function(tokenType, lineNumber) { var i, len, token, tokens; if (tokenType == null) { tokenType = null; } if (lineNumber == null) { lineNumber = null; } lineNumber = lineNumber != null ? lineNumber : this.lineNumber; if (tokenType == null) { return this.tokensByLine[lineNumber] != null; } else { tokens = this.tokensByLine[lineNumber]; if (tokens == null) { return null; } for (i = 0, len = tokens.length; i < len; i++) { token = tokens[i]; if (token[0] === tokenType) { return true; } } return false; } }; LineApi.prototype.getLineTokens = function() { return this.tokensByLine[this.lineNumber] || []; }; return LineApi; })(); BaseLinter = require('./base_linter.coffee'); configStatement = /coffeelint:\s*((disable|enable)(-line)?)(?:=([\w\s,]*))?/; configShortcuts = [[/\#.*noqa/, 'coffeelint: disable-line']]; module.exports = LineLinter = (function(superClass) { extend(LineLinter, superClass); LineLinter.getDirective = function(line) { var i, len, ref, replacement, shortcut; for (i = 0, len = configShortcuts.length; i < len; i++) { ref = configShortcuts[i], shortcut = ref[0], replacement = ref[1]; if (line.match(shortcut)) { return configStatement.exec(replacement); } } return configStatement.exec(line); }; function LineLinter(source, config, rules, tokensByLine, literate) { if (literate == null) { literate = false; } LineLinter.__super__.constructor.call(this, source, config, rules); this.lineApi = new LineApi(source, config, tokensByLine, literate); this.inlineConfig = { enable: {}, disable: {}, 'enable-line': {}, 'disable-line': {} }; } LineLinter.prototype.acceptRule = function(rule) { return typeof rule.lintLine === 'function'; }; LineLinter.prototype.lint = function() { var error, errors, i, j, len, len1, line, lineNumber, ref, ref1; errors = []; ref = this.lineApi.lines; for (lineNumber = i = 0, len = ref.length; i < len; lineNumber = ++i) { line = ref[lineNumber]; this.lineApi.lineNumber = this.lineNumber = lineNumber; this.lineApi.line = this.lineApi.lines[lineNumber]; this.lineApi.maintainClassContext(line); this.collectInlineConfig(line); ref1 = this.lintLine(line); for (j = 0, len1 = ref1.length; j < len1; j++) { error = ref1[j]; errors.push(error); } } return errors; }; LineLinter.prototype.lintLine = function(line) { var errors, i, len, ref, rule, v; errors = []; ref = this.rules; for (i = 0, len = ref.length; i < len; i++) { rule = ref[i]; v = this.normalizeResult(rule, rule.lintLine(line, this.lineApi)); if (v != null) { errors.push(v); } } return errors; }; LineLinter.prototype.collectInlineConfig = function(line) { var cmd, i, len, r, ref, result, rules; result = this.constructor.getDirective(line); if (result != null) { cmd = result[1]; rules = []; if (result[4] != null) { ref = result[4].split(','); for (i = 0, len = ref.length; i < len; i++) { r = ref[i]; rules.push(r.replace(/^\s+|\s+$/g, '')); } } this.inlineConfig[cmd][this.lineNumber] = rules; } return null; }; LineLinter.prototype.createError = function(rule, attrs) { var ref; if (attrs == null) { attrs = {}; } attrs.lineNumber = this.lineNumber + 1; attrs.level = (ref = this.config[rule]) != null ? ref.level : void 0; return LineLinter.__super__.createError.call(this, rule, attrs); }; return LineLinter; })(BaseLinter); },{"./base_linter.coffee":3}],8:[function(require,module,exports){ var ERROR, IGNORE, WARN; ERROR = 'error'; WARN = 'warn'; IGNORE = 'ignore'; module.exports = { coffeescript_error: { level: ERROR, message: '' } }; },{}],9:[function(require,module,exports){ var ArrowSpacing; module.exports = ArrowSpacing = (function() { function ArrowSpacing() {} ArrowSpacing.prototype.rule = { name: 'arrow_spacing', level: 'ignore', message: 'Function arrows (-> and =>) must be spaced properly', description: '<p>This rule checks to see that there is spacing before and after\nthe arrow operator that declares a function. This rule is disabled\nby default.</p> <p>Note that if arrow_spacing is enabled, and you\npass an empty function as a parameter, arrow_spacing will accept\neither a space or no space in-between the arrow operator and the\nparenthesis</p>\n<pre><code># Both of this will not trigger an error,\n# even with arrow_spacing enabled.\nx(-> 3)\nx( -> 3)\n\n# However, this will trigger an error\nx((a,b)-> 3)\n</code>\n</pre>' }; ArrowSpacing.prototype.tokens = ['->', '=>']; ArrowSpacing.prototype.lintToken = function(token, tokenApi) { var pp; pp = tokenApi.peek(-1); if (!pp) { return; } if (!token.spaced && tokenApi.peek(1)[0] === 'INDENT' && tokenApi.peek(2)[0] === 'OUTDENT') { return null; } else if (!(((token.spaced != null) || (token.newLine != null)) && (((pp.spaced != null) || pp[0] === 'TERMINATOR') || (pp.generated != null) || pp[0] === 'INDENT' || (pp[1] === '(' && (pp.generated == null))))) { return true; } else { return null; } }; return ArrowSpacing; })(); },{}],10:[function(require,module,exports){ var BracesSpacing; module.exports = BracesSpacing = (function() { function BracesSpacing() {} BracesSpacing.prototype.rule = { name: 'braces_spacing', level: 'ignore', spaces: 0, empty_object_spaces: 0, message: 'Curly braces must have the proper spacing', description: 'This rule checks to see that there is the proper spacing inside\ncurly braces. The spacing amount is specified by "spaces".\nThe spacing amount for empty objects is specified by\n"empty_object_spaces".\n\n<pre><code>\n# Spaces is 0\n{a: b} # Good\n{a: b } # Bad\n{ a: b} # Bad\n{ a: b } # Bad\n\n# Spaces is 1\n{a: b} # Bad\n{a: b } # Bad\n{ a: b} # Bad\n{ a: b } # Good\n{ a: b } # Bad\n{ a: b } # Bad\n{ a: b } # Bad\n\n# Empty Object Spaces is 0\n{} # Good\n{ } # Bad\n\n# Empty Object Spaces is 1\n{} # Bad\n{ } # Good\n</code></pre>\n\nThis rule is disabled by default.' }; BracesSpacing.prototype.tokens = ['{', '}']; BracesSpacing.prototype.distanceBetweenTokens = function(firstToken, secondToken) { return secondToken[2].first_column - firstToken[2].last_column - 1; }; BracesSpacing.prototype.findNearestToken = function(token, tokenApi, difference) { var nearestToken, totalDifference; totalDifference = 0; while (true) { totalDifference += difference; nearestToken = tokenApi.peek(totalDifference); if (nearestToken[0] === 'OUTDENT' || (nearestToken.generated != null)) { continue; } return nearestToken; } }; BracesSpacing.prototype.tokensOnSameLine = function(firstToken, secondToken) { return firstToken[2].first_line === secondToken[2].first_line; }; BracesSpacing.prototype.getExpectedSpaces = function(tokenApi, firstToken, secondToken) { var config, ref; config = tokenApi.config[this.rule.name]; if (firstToken[0] === '{' && secondToken[0] === '}') { return (ref = config.empty_object_spaces) != null ? ref : config.spaces; } else { return config.spaces; } }; BracesSpacing.prototype.lintToken = function(token, tokenApi) { var actual, expected, firstToken, msg, ref, secondToken; if (token.generated) { return null; } ref = token[0] === '{' ? [token, this.findNearestToken(token, tokenApi, 1)] : [this.findNearestToken(token, tokenApi, -1), token], firstToken = ref[0], secondToken = ref[1]; if (!this.tokensOnSameLine(firstToken, secondToken)) { return null; } expected = this.getExpectedSpaces(tokenApi, firstToken, secondToken); actual = this.distanceBetweenTokens(firstToken, secondToken); if (actual === expected) { return null; } else { msg = "There should be " + expected + " space"; if (expected !== 1) { msg += 's'; } msg += " inside \"" + token[0] + "\""; return { context: msg }; } }; return BracesSpacing; })(); },{}],11:[function(require,module,exports){ var CamelCaseClasses, regexes; regexes = { camelCase: /^[A-Z_][a-zA-Z\d]*$/ }; module.exports = CamelCaseClasses = (function() { function CamelCaseClasses() {} CamelCaseClasses.prototype.rule = { name: 'camel_case_classes', level: 'error', message: 'Class name should be UpperCamelCased', description: 'This rule mandates that all class names are UpperCamelCased.\nCamel casing class names is a generally accepted way of\ndistinguishing constructor functions - which require the \'new\'\nprefix to behave properly - from plain old functions.\n<pre>\n<code># Good!\nclass BoaConstrictor\n\n# Bad!\nclass boaConstrictor\n</code>\n</pre>\nThis rule is enabled by default.' }; CamelCaseClasses.prototype.tokens = ['CLASS']; CamelCaseClasses.prototype.lintToken = function(token, tokenApi) { var className, offset, ref, ref1, ref2; if ((token.newLine != null) || ((ref = tokenApi.peek()[0]) === 'INDENT' || ref === 'EXTENDS')) { return null; } className = null; offset = 1; while (!className) { if (((ref1 = tokenApi.peek(offset + 1)) != null ? ref1[0] : void 0) === '.') { offset += 2; } else if (((ref2 = tokenApi.peek(offset)) != null ? ref2[0] : void 0) === '@') { offset += 1; } else { className = tokenApi.peek(offset)[1]; } } if (!regexes.camelCase.test(className)) { return { context: "class name: " + className }; } }; return CamelCaseClasses; })(); },{}],12:[function(require,module,exports){ var ColonAssignmentSpacing; module.exports = ColonAssignmentSpacing = (function() { function ColonAssignmentSpacing() {} ColonAssignmentSpacing.prototype.rule = { name: 'colon_assignment_spacing', level: 'ignore', message: 'Colon assignment without proper spacing', spacing: { left: 0, right: 0 }, description: '<p>This rule checks to see that there is spacing before and\nafter the colon in a colon assignment (i.e., classes, objects).\nThe spacing amount is specified by\nspacing.left and spacing.right, respectively.\nA zero value means no spacing required.\n</p>\n<pre><code>\n#\n# If spacing.left and spacing.right is 1\n#\n\n# Doesn\'t throw an error\nobject = {spacing : true}\nclass Dog\n canBark : true\n\n# Throws an error\nobject = {spacing: true}\nclass Cat\n canBark: false\n</code></pre>' }; ColonAssignmentSpacing.prototype.tokens = [':']; ColonAssignmentSpacing.prototype.lintToken = function(token, tokenApi) { var checkSpacing, getSpaceFromToken, isLeftSpaced, isRightSpaced, leftSpacing, nextToken, previousToken, ref, ref1, rightSpacing, spaceRules; spaceRules = tokenApi.config[this.rule.name].spacing; previousToken = tokenApi.peek(-1); nextToken = tokenApi.peek(1); getSpaceFromToken = function(direction) { switch (direction) { case 'left': return token[2].first_column - previousToken[2].last_column - 1; case 'right': return nextToken[2].first_column - token[2].first_column - 1; } }; checkSpacing = function(direction) { var isSpaced, spacing; spacing = getSpaceFromToken(direction); isSpaced = spacing < 0 ? true : spacing === parseInt(spaceRules[direction]); return [isSpaced, spacing]; }; ref = checkSpacing('left'), isLeftSpaced = ref[0], leftSpacing = ref[1]; ref1 = checkSpacing('right'), isRightSpaced = ref1[0], rightSpacing = ref1[1]; if (isLeftSpaced && isRightSpaced) { return null; } else { return { context: "Incorrect spacing around column " + token[2].first_column }; } }; return ColonAssignmentSpacing; })(); },{}],13:[function(require,module,exports){ var CyclomaticComplexity; module.exports = CyclomaticComplexity = (function() { function CyclomaticComplexity() {} CyclomaticComplexity.prototype.rule = { name: 'cyclomatic_complexity', level: 'ignore', message: 'The cyclomatic complexity is too damn high', value: 10, description: 'Examine the complexity of your function.' }; CyclomaticComplexity.prototype.getComplexity = function(node) { var complexity, name, ref; name = this.astApi.getNodeName(node); complexity = name === 'If' || name === 'While' || name === 'For' || name === 'Try' ? 1 : name === 'Op' && ((ref = node.operator) === '&&' || ref === '||') ? 1 : name === 'Switch' ? node.cases.length : 0; return complexity; }; CyclomaticComplexity.prototype.lintAST = function(node, astApi) { this.astApi = astApi; this.lintNode(node); return void 0; }; CyclomaticComplexity.prototype.lintNode = function(node) { var complexity, error, name, ref, rule; name = (ref = this.astApi) != null ? ref.getNodeName(node) : void 0; complexity = this.getComplexity(node); node.eachChild((function(_this) { return function(childNode) { var childComplexity, ref1; childComplexity = _this.lintNode(childNode); if (((ref1 = _this.astApi) != null ? ref1.getNodeName(childNode) : void 0) !== 'Code') { return complexity += childComplexity; } }; })(this)); rule = this.astApi.config[this.rule.name]; if (name === 'Code' && complexity >= rule.value) { error = this.astApi.createError({ context: complexity + 1, lineNumber: node.locationData.first_line + 1, lineNumberEnd: node.locationData.last_line + 1 }); if (error) { this.errors.push(error); } } return complexity; }; return CyclomaticComplexity; })(); },{}],14:[function(require,module,exports){ var DuplicateKey; module.exports = DuplicateKey = (function() { DuplicateKey.prototype.rule = { name: 'duplicate_key', level: 'error', message: 'Duplicate key defined in object or class', description: 'Prevents defining duplicate keys in object literals and classes' }; DuplicateKey.prototype.tokens = ['IDENTIFIER', 'PROPERTY', '{', '}']; function DuplicateKey() { this.braceScopes = []; } DuplicateKey.prototype.lintToken = function(arg, tokenApi) { var type; type = arg[0]; if (type === '{' || type === '}') { this.lintBrace.apply(this, arguments); return void 0; } if (type === 'IDENTIFIER' || type === 'PROPERTY') { return this.lintIdentifier.apply(this, arguments); } }; DuplicateKey.prototype.lintIdentifier = function(token, tokenApi) { var key, nextToken, previousToken; key = token[1]; if (this.currentScope == null) { return null; } nextToken = tokenApi.peek(1); if (nextToken[1] !== ':') { return null; } previousToken = tokenApi.peek(-1); if (previousToken[0] === '@') { key = "@" + key; } key = "identifier-" + key; if (this.currentScope[key]) { return true; } else { this.currentScope[key] = token; return null; } }; DuplicateKey.prototype.lintBrace = function(token) { if (token[0] === '{') { if (this.currentScope != null) { this.braceScopes.push(this.currentScope); } this.currentScope = {}; } else { this.currentScope = this.braceScopes.pop(); } return null; }; return DuplicateKey; })(); },{}],15:[function(require,module,exports){ var EmptyConstructorNeedsParens; module.exports = EmptyConstructorNeedsParens = (function() { function EmptyConstructorNeedsParens() {} EmptyConstructorNeedsParens.prototype.rule = { name: 'empty_constructor_needs_parens', level: 'ignore', message: 'Invoking a constructor without parens and without arguments', description: 'Requires constructors with no parameters to include the parens' }; EmptyConstructorNeedsParens.prototype.tokens = ['UNARY']; EmptyConstructorNeedsParens.prototype.lintToken = function(token, tokenApi) { var identIndex, isIdent, nextToken, peek, ref, ref1, ref2; if (token[1] === 'new') { peek = tokenApi.peek.bind(tokenApi); identIndex = 1; while (true) { isIdent = (ref = (ref1 = peek(identIndex)) != null ? ref1[0] : void 0) === 'IDENTIFIER' || ref === 'PROPERTY'; nextToken = peek(identIndex + 1); if (isIdent) { if ((nextToken != null ? nextToken[0] : void 0) === '.') { identIndex += 2; continue; } if ((nextToken != null ? nextToken[0] : void 0) === 'INDEX_START') { while (((ref2 = peek(identIndex)) != null ? ref2[0] : void 0) !== 'INDEX_END') { identIndex++; } continue; } } break; } if (isIdent && (nextToken != null)) { return this.handleExpectedCallStart(nextToken); } } }; EmptyConstructorNeedsParens.prototype.handleExpectedCallStart = function(isCallStart) { if (isCallStart[0] !== 'CALL_START') { return true; } }; return EmptyConstructorNeedsParens; })(); },{}],16:[function(require,module,exports){ var EnsureComprehensions, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; module.exports = EnsureComprehensions = (function() { function EnsureComprehensions() {} EnsureComprehensions.prototype.rule = { name: 'ensure_comprehensions', level: 'warn', message: 'Comprehensions must have parentheses around them', description: 'This rule makes sure that parentheses are around comprehensions.' }; EnsureComprehensions.prototype.tokens = ['FOR']; EnsureComprehensions.prototype.forBlock = false; EnsureComprehensions.prototype.li