@coffeelint/cli
Version:
Lint your CoffeeScript
126 lines (114 loc) • 4.22 kB
JavaScript
(function() {
var NoImplicitBraces,
slice = [].slice;
module.exports = NoImplicitBraces = (function() {
class NoImplicitBraces {
constructor() {
this.isClass = false;
this.className = '';
}
lintToken(token, tokenApi) {
var _type, _val, c, lineNum, peekIdent, prevToken, ref, type, val;
[type, val, lineNum] = token;
if (type === 'OUTDENT' || type === 'INDENT' || type === 'CLASS') {
return this.trackClass(...arguments);
}
// reset "className" if class uses EXTENDS keyword
if (type === 'EXTENDS') {
this.className = '';
return;
}
// If we're looking at an IDENTIFIER, and we're in a class, and we've not
// set a className (or the previous non-identifier was 'EXTENDS', set the
// current identifier as the class name)
if ((type === 'IDENTIFIER' || type === 'PROPERTY') && this.isClass && this.className === '') {
// Backtrack to get the full classname
c = 0;
while ((ref = tokenApi.peek(c)[0]) === 'IDENTIFIER' || ref === 'PROPERTY' || ref === '.') {
this.className += tokenApi.peek(c)[1];
c++;
}
}
if (token.generated && type === '{') {
// If strict mode is set to false it allows implicit braces when the
// object is declared over multiple lines.
if (!tokenApi.config[this.rule.name].strict) {
[prevToken] = tokenApi.peek(-1);
if (prevToken === 'INDENT' || prevToken === 'TERMINATOR') {
return;
}
}
if (this.isClass) {
// The way CoffeeScript generates tokens for classes
// is a bit weird. It generates '{' tokens around instance
// methods (also known as the prototypes of an Object).
[prevToken] = tokenApi.peek(-1);
// If there is a TERMINATOR token right before the '{' token
if (prevToken === 'TERMINATOR') {
return;
}
peekIdent = '';
c = -2;
// Go back until you grab all the tokens with IDENTIFIER,
// PROPERTY or '.'
while (([_type, _val] = tokenApi.peek(c))) {
if (_type !== 'IDENTIFIER' && _type !== 'PROPERTY' && _type !== '.') {
break;
}
peekIdent = _val + peekIdent;
c--;
}
if (peekIdent === this.className) {
return;
}
}
return {
token: tokenApi.peek(c + 1)
};
}
}
trackClass(token, tokenApi) {
var ln, n0, n1, ref, ref1, ref2;
ref = [token, tokenApi.peek()], (ref1 = ref[0], [n0] = ref1, [ln] = slice.call(ref1, -1)), (ref2 = ref[1], [n1] = ref2);
if (n0 === 'INDENT') {
this.dent++;
}
if (n0 === 'OUTDENT') {
this.dent--;
}
if (this.dent === 0 && n0 === 'OUTDENT' && n1 === 'TERMINATOR') {
this.isClass = false;
}
if (n0 === 'CLASS') {
this.isClass = true;
this.className = '';
}
return null;
}
};
NoImplicitBraces.prototype.rule = {
type: 'style',
name: 'no_implicit_braces',
level: 'ignore',
message: 'Implicit braces are forbidden',
strict: true,
description: `This rule prohibits implicit braces when declaring object literals.
Implicit braces can make code more difficult to understand,
especially when used in combination with optional parenthesis.
<pre>
<code># Do you find this code ambiguous? Is it a
# function call with three arguments or four?
myFunction a, b, 1:2, 3:4
# While the same code written in a more
# explicit manner has no ambiguity.
myFunction(a, b, {1:2, 3:4})
</code>
</pre>
Implicit braces are permitted by default, since their use is
idiomatic CoffeeScript.`
};
NoImplicitBraces.prototype.tokens = ['{', 'OUTDENT', 'INDENT', 'CLASS', 'IDENTIFIER', 'PROPERTY', 'EXTENDS'];
NoImplicitBraces.prototype.dent = 0;
return NoImplicitBraces;
}).call(this);
}).call(this);