coffeelint 0.0.6 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -15
- data/.gitmodules +1 -1
- data/README.md +19 -7
- data/bin/coffeelint.rb +69 -11
- data/coffeelint/src/coffeelint.coffee +248 -31
- data/lib/coffeelint.rb +38 -18
- data/lib/coffeelint/version.rb +1 -1
- data/spec/coffeelint_spec.rb +11 -0
- metadata +46 -59
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
YjBmNzVkZjRmMGJlOTRhOWRjMzJjNTJjZWU4MTE4MWQ2ZWUxOGJiNTRkOGUw
|
10
|
-
MDdhODRmYjg3OGU0OTdmMWViYTg5ODEyOWExYTI3ZWUyMjY1YTBiMjk3N2Q0
|
11
|
-
MmViOWQzOTc5MTlkOTJhYzE2M2RmZTE1OGFmMGMzY2YwMWM1NWU=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YmViOTViODYyOWMxNDg0YTVhZDAyNmRjMTkzZGI0NDY0ZDk5OGU1NDE4NTQ4
|
14
|
-
ZmExMWUyYTVkZDVjYmRiZGQ5OGM5MzFmOTkyODY5YzExYzA4NTMzMTcwZjNl
|
15
|
-
YTU3YjdhMjMxN2ExZThkNTY4NzhkMDc3MTRhYmUzNjE1NmY4OGY=
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bb1cdf1d80f8dca2f87ad8233000a3e659f9ad05
|
4
|
+
data.tar.gz: cfb06027c385cc3bbe4ab089a721eb6dbe1ac531
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 54efaacd4b1700ac6d88e18c03e75b1cc831abd1b43f1e65618644fd03a838a9e615f8344fb0b22147e01d5e3a717fbfe857085db1e96fc2a5cf423ce180cf36
|
7
|
+
data.tar.gz: 84854349a891d82f5acd8d1cc0582d758fd4857c60580ff2109246feab7fdc70fafa03875a6b18bb8095659887b74432ea97fca769489a024d875e336c86b7d5
|
data/.gitmodules
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Coffeelint
|
1
|
+
# Coffeelint [![Gem Version](https://badge.fury.io/rb/coffeelint.png)](http://badge.fury.io/rb/coffeelint)
|
2
2
|
|
3
3
|
Coffeelint is a set of simple ruby bindings for [coffeelint](https://github.com/clutchski/coffeelint).
|
4
4
|
|
@@ -25,18 +25,29 @@ Or install it yourself as:
|
|
25
25
|
There are a few different uses of coffeelint.
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
lint_report = Coffeelint.lint(coffeescript source code)
|
29
|
-
lint_report = Coffeelint.lint_file(filename of coffeescript source)
|
30
|
-
lint_reports = Coffeelint.lint_dir(directory)
|
31
|
-
Coffeelint.lint_dir(directory) do |filename, lint_report|
|
28
|
+
lint_report = Coffeelint.lint(coffeescript source code, [config options])
|
29
|
+
lint_report = Coffeelint.lint_file(filename of coffeescript source, [config_options])
|
30
|
+
lint_reports = Coffeelint.lint_dir(directory, [config_options])
|
31
|
+
Coffeelint.lint_dir(directory, [config_options]) do |filename, lint_report|
|
32
32
|
puts filename
|
33
33
|
puts lint_report
|
34
34
|
Coffeelint.display_test_results(filename, lint_report)
|
35
35
|
end
|
36
|
-
Coffeelint.run_test(filename of coffeescript source) # Run tests and print pretty results (return true/false)
|
37
|
-
Coffeelint.run_test_suite(directory) # Runs a pretty report recursively for a directory (return true/false)
|
36
|
+
Coffeelint.run_test(filename of coffeescript source, [config_options]) # Run tests and print pretty results (return true/false)
|
37
|
+
Coffeelint.run_test_suite(directory, [config_options]) # Runs a pretty report recursively for a directory (return true/false)
|
38
38
|
```
|
39
39
|
|
40
|
+
### Config Options
|
41
|
+
|
42
|
+
The coffeelint gem takes the same config options as coffeelint. The only
|
43
|
+
addition is the config_file parameter. If you call coffeelint like:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
Coffeelint.run_test_suite(directory, :config_file => 'coffeelint_config.json')
|
47
|
+
```
|
48
|
+
|
49
|
+
Then it will load the config options from that file.
|
50
|
+
|
40
51
|
Additionally, if you are using rails you also get the rake task:
|
41
52
|
|
42
53
|
rake coffeelint
|
@@ -47,6 +58,7 @@ Finally, there is a command line utility that allows you to run standalone tests
|
|
47
58
|
|
48
59
|
coffeelint.rb <filename>
|
49
60
|
coffeelint.rb -r <directory>
|
61
|
+
coffeelint.rb -f <config.json> [-r] <fname-or-directory>
|
50
62
|
|
51
63
|
## Contributing
|
52
64
|
|
data/bin/coffeelint.rb
CHANGED
@@ -1,21 +1,79 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'coffeelint'
|
4
|
+
require 'coffeelint/version'
|
4
5
|
require 'optparse'
|
5
6
|
|
6
|
-
options = {
|
7
|
-
|
8
|
-
|
7
|
+
options = {
|
8
|
+
:recursive => false,
|
9
|
+
:noconfig => false,
|
10
|
+
:stdin => false,
|
11
|
+
:quiet => false,
|
12
|
+
}
|
9
13
|
|
10
|
-
|
11
|
-
|
14
|
+
linter_options = {}
|
15
|
+
|
16
|
+
opt_parser = OptionParser.new do |opts|
|
17
|
+
opts.banner = "Usage: coffeelint.rb [options] source [...]"
|
18
|
+
|
19
|
+
=begin
|
20
|
+
-f, --file Specify a custom configuration file.
|
21
|
+
--noconfig Ignores the environment variable COFFEELINT_CONFIG. [boolean]
|
22
|
+
-h, --help Print help information.
|
23
|
+
-v, --version Print current version number.
|
24
|
+
-r Recursively lint .coffee files in subdirectories. [boolean]
|
25
|
+
--csv Use the csv reporter. [boolean]
|
26
|
+
--jslint Use the JSLint XML reporter. [boolean]
|
27
|
+
--nocolor Don't colorize the output [boolean]
|
28
|
+
-s, --stdin Lint the source from stdin [boolean]
|
29
|
+
-q, --quiet Only print errors. [boolean]
|
30
|
+
=end
|
31
|
+
|
32
|
+
|
33
|
+
opts.on "-f", "--file", "Specify a custom configuration file." do |f|
|
34
|
+
linter_options[:config_file] = f
|
35
|
+
end
|
36
|
+
|
37
|
+
=begin
|
38
|
+
opts.on "--noconfig", "Ignores the environment variabel COFFEELINT_CONFIG." do |f|
|
39
|
+
options[:noconfig] = true
|
40
|
+
end
|
41
|
+
=end
|
42
|
+
|
43
|
+
opts.on_tail "-h", "--help", "Print help information." do
|
44
|
+
puts opts
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on_tail "-v", "--version", "Print current version number." do
|
49
|
+
puts Coffeelint::VERSION
|
50
|
+
exit
|
12
51
|
end
|
13
|
-
end.parse!
|
14
52
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
53
|
+
opts.on '-r', "Recursively lint .coffee files in subdirectories." do
|
54
|
+
options[:recursive] = true
|
55
|
+
end
|
56
|
+
|
57
|
+
=begin
|
58
|
+
opts.on '-s', '--stdin', "Lint the source from stdin" do
|
59
|
+
options[:stdin] = true
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on '-q', '--quiet', 'Only print errors.' do
|
63
|
+
options[:quiet] = true
|
64
|
+
end
|
65
|
+
=end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
opt_parser.parse!
|
70
|
+
|
71
|
+
if ARGV.length > 0
|
72
|
+
ARGV.each do |file|
|
73
|
+
if options[:recursive]
|
74
|
+
Coffeelint.run_test_suite(file, linter_options)
|
75
|
+
else
|
76
|
+
Coffeelint.run_test(file, linter_options)
|
77
|
+
end
|
20
78
|
end
|
21
79
|
end
|
@@ -23,7 +23,7 @@ else
|
|
23
23
|
|
24
24
|
|
25
25
|
# The current version of Coffeelint.
|
26
|
-
coffeelint.VERSION = "0.5.
|
26
|
+
coffeelint.VERSION = "0.5.7"
|
27
27
|
|
28
28
|
|
29
29
|
# CoffeeLint error levels.
|
@@ -38,41 +38,144 @@ coffeelint.RULES = RULES =
|
|
38
38
|
no_tabs :
|
39
39
|
level : ERROR
|
40
40
|
message : 'Line contains tab indentation'
|
41
|
+
description: """
|
42
|
+
This rule forbids tabs in indentation. Enough said. It is enabled by
|
43
|
+
default.
|
44
|
+
"""
|
41
45
|
|
42
46
|
no_trailing_whitespace :
|
43
47
|
level : ERROR
|
44
48
|
message : 'Line ends with trailing whitespace'
|
45
49
|
allowed_in_comments : false
|
50
|
+
description: """
|
51
|
+
This rule forbids trailing whitespace in your code, since it is
|
52
|
+
needless cruft. It is enabled by default.
|
53
|
+
"""
|
46
54
|
|
47
55
|
max_line_length :
|
48
56
|
value: 80
|
49
57
|
level : ERROR
|
50
58
|
message : 'Line exceeds maximum allowed length'
|
59
|
+
description: """
|
60
|
+
This rule imposes a maximum line length on your code. <a
|
61
|
+
href="http://www.python.org/dev/peps/pep-0008/">Python's style
|
62
|
+
guide</a> does a good job explaining why you might want to limit the
|
63
|
+
length of your lines, though this is a matter of taste.
|
64
|
+
|
65
|
+
Lines can be no longer than eighty characters by default.
|
66
|
+
"""
|
51
67
|
|
52
68
|
camel_case_classes :
|
53
69
|
level : ERROR
|
54
70
|
message : 'Class names should be camel cased'
|
55
|
-
|
71
|
+
description: """
|
72
|
+
This rule mandates that all class names are camel cased. Camel
|
73
|
+
casing class names is a generally accepted way of distinguishing
|
74
|
+
constructor functions - which require the 'new' prefix to behave
|
75
|
+
properly - from plain old functions.
|
76
|
+
<pre>
|
77
|
+
<code># Good!
|
78
|
+
class BoaConstrictor
|
79
|
+
|
80
|
+
# Bad!
|
81
|
+
class boaConstrictor
|
82
|
+
</code>
|
83
|
+
</pre>
|
84
|
+
This rule is enabled by default.
|
85
|
+
"""
|
56
86
|
indentation :
|
57
87
|
value : 2
|
58
88
|
level : ERROR
|
59
89
|
message : 'Line contains inconsistent indentation'
|
90
|
+
description: """
|
91
|
+
This rule imposes a standard number of spaces to be used for
|
92
|
+
indentation. Since whitespace is significant in CoffeeScript, it's
|
93
|
+
critical that a project chooses a standard indentation format and
|
94
|
+
stays consistent. Other roads lead to darkness. <pre> <code>#
|
95
|
+
Enabling this option will prevent this ugly
|
96
|
+
# but otherwise valid CoffeeScript.
|
97
|
+
twoSpaces = () ->
|
98
|
+
fourSpaces = () ->
|
99
|
+
eightSpaces = () ->
|
100
|
+
'this is valid CoffeeScript'
|
101
|
+
|
102
|
+
</code>
|
103
|
+
</pre>
|
104
|
+
Two space indentation is enabled by default.
|
105
|
+
"""
|
60
106
|
|
61
107
|
no_implicit_braces :
|
62
108
|
level : IGNORE
|
63
109
|
message : 'Implicit braces are forbidden'
|
110
|
+
description: """
|
111
|
+
This rule prohibits implicit braces when declaring object literals.
|
112
|
+
Implicit braces can make code more difficult to understand,
|
113
|
+
especially when used in combination with optional parenthesis.
|
114
|
+
<pre>
|
115
|
+
<code># Do you find this code ambiguous? Is it a
|
116
|
+
# function call with three arguments or four?
|
117
|
+
myFunction a, b, 1:2, 3:4
|
118
|
+
|
119
|
+
# While the same code written in a more
|
120
|
+
# explicit manner has no ambiguity.
|
121
|
+
myFunction(a, b, {1:2, 3:4})
|
122
|
+
</code>
|
123
|
+
</pre>
|
124
|
+
Implicit braces are permitted by default, since their use is
|
125
|
+
idiomatic CoffeeScript.
|
126
|
+
"""
|
64
127
|
|
65
128
|
no_trailing_semicolons:
|
66
129
|
level : ERROR
|
67
130
|
message : 'Line contains a trailing semicolon'
|
131
|
+
description: """
|
132
|
+
This rule prohibits trailing semicolons, since they are needless
|
133
|
+
cruft in CoffeeScript.
|
134
|
+
<pre>
|
135
|
+
<code># This semicolon is meaningful.
|
136
|
+
x = '1234'; console.log(x)
|
137
|
+
|
138
|
+
# This semicolon is redundant.
|
139
|
+
alert('end of line');
|
140
|
+
</code>
|
141
|
+
</pre>
|
142
|
+
Trailing semicolons are forbidden by default.
|
143
|
+
"""
|
68
144
|
|
69
145
|
no_plusplus:
|
70
146
|
level : IGNORE
|
71
147
|
message : 'The increment and decrement operators are forbidden'
|
148
|
+
description: """
|
149
|
+
This rule forbids the increment and decrement arithmetic operators.
|
150
|
+
Some people believe the <tt>++</tt> and <tt>--</tt> to be cryptic
|
151
|
+
and the cause of bugs due to misunderstandings of their precedence
|
152
|
+
rules.
|
153
|
+
This rule is disabled by default.
|
154
|
+
"""
|
72
155
|
|
73
156
|
no_throwing_strings:
|
74
157
|
level : ERROR
|
75
158
|
message : 'Throwing strings is forbidden'
|
159
|
+
description: """
|
160
|
+
This rule forbids throwing string literals or interpolations. While
|
161
|
+
JavaScript (and CoffeeScript by extension) allow any expression to
|
162
|
+
be thrown, it is best to only throw <a
|
163
|
+
href="https://developer.mozilla.org
|
164
|
+
/en/JavaScript/Reference/Global_Objects/Error"> Error</a> objects,
|
165
|
+
because they contain valuable debugging information like the stack
|
166
|
+
trace. Because of JavaScript's dynamic nature, CoffeeLint cannot
|
167
|
+
ensure you are always throwing instances of <tt>Error</tt>. It will
|
168
|
+
only catch the simple but real case of throwing literal strings.
|
169
|
+
<pre>
|
170
|
+
<code># CoffeeLint will catch this:
|
171
|
+
throw "i made a boo boo"
|
172
|
+
|
173
|
+
# ... but not this:
|
174
|
+
throw getSomeString()
|
175
|
+
</code>
|
176
|
+
</pre>
|
177
|
+
This rule is enabled by default.
|
178
|
+
"""
|
76
179
|
|
77
180
|
cyclomatic_complexity:
|
78
181
|
value : 10
|
@@ -82,15 +185,39 @@ coffeelint.RULES = RULES =
|
|
82
185
|
no_backticks:
|
83
186
|
level : ERROR
|
84
187
|
message : 'Backticks are forbidden'
|
188
|
+
description: """
|
189
|
+
Backticks allow snippets of JavaScript to be embedded in
|
190
|
+
CoffeeScript. While some folks consider backticks useful in a few
|
191
|
+
niche circumstances, they should be avoided because so none of
|
192
|
+
JavaScript's "bad parts", like <tt>with</tt> and <tt>eval</tt>,
|
193
|
+
sneak into CoffeeScript.
|
194
|
+
This rule is enabled by default.
|
195
|
+
"""
|
85
196
|
|
86
197
|
line_endings:
|
87
198
|
level : IGNORE
|
88
199
|
value : 'unix' # or 'windows'
|
89
200
|
message : 'Line contains incorrect line endings'
|
90
|
-
|
201
|
+
description: """
|
202
|
+
This rule ensures your project uses only <tt>windows</tt> or
|
203
|
+
<tt>unix</tt> line endings. This rule is disabled by default.
|
204
|
+
"""
|
91
205
|
no_implicit_parens :
|
92
206
|
level : IGNORE
|
93
207
|
message : 'Implicit parens are forbidden'
|
208
|
+
description: """
|
209
|
+
This rule prohibits implicit parens on function calls.
|
210
|
+
<pre>
|
211
|
+
<code># Some folks don't like this style of coding.
|
212
|
+
myFunction a, b, c
|
213
|
+
|
214
|
+
# And would rather it always be written like this:
|
215
|
+
myFunction(a, b, c)
|
216
|
+
</code>
|
217
|
+
</pre>
|
218
|
+
Implicit parens are permitted by default, since their use is
|
219
|
+
idiomatic CoffeeScript.
|
220
|
+
"""
|
94
221
|
|
95
222
|
empty_constructor_needs_parens :
|
96
223
|
level : IGNORE
|
@@ -103,6 +230,19 @@ coffeelint.RULES = RULES =
|
|
103
230
|
no_empty_param_list :
|
104
231
|
level : IGNORE
|
105
232
|
message : 'Empty parameter list is forbidden'
|
233
|
+
description: """
|
234
|
+
This rule prohibits empty parameter lists in function definitions.
|
235
|
+
<pre>
|
236
|
+
<code># The empty parameter list in here is unnecessary:
|
237
|
+
myFunction = () ->
|
238
|
+
|
239
|
+
# We might favor this instead:
|
240
|
+
myFunction = ->
|
241
|
+
</code>
|
242
|
+
</pre>
|
243
|
+
Empty parameter lists are permitted by default.
|
244
|
+
"""
|
245
|
+
|
106
246
|
|
107
247
|
space_operators :
|
108
248
|
level : IGNORE
|
@@ -124,10 +264,33 @@ coffeelint.RULES = RULES =
|
|
124
264
|
no_stand_alone_at :
|
125
265
|
level : IGNORE
|
126
266
|
message : '@ must not be used stand alone'
|
267
|
+
description: """
|
268
|
+
This rule checks that no stand alone @ are in use, they are
|
269
|
+
discouraged. Further information in CoffeScript issue <a
|
270
|
+
href="https://github.com/jashkenas/coffee-script/issues/1601">
|
271
|
+
#1601</a>
|
272
|
+
"""
|
127
273
|
|
128
274
|
arrow_spacing :
|
129
275
|
level : IGNORE
|
130
276
|
message : 'Function arrow (->) must be spaced properly'
|
277
|
+
description: """
|
278
|
+
<p>This rule checks to see that there is spacing before and after
|
279
|
+
the arrow operator that declares a function. This rule is disabled
|
280
|
+
by default.</p> <p>Note that if arrow_spacing is enabled, and you
|
281
|
+
pass an empty function as a parameter, arrow_spacing will accept
|
282
|
+
either a space or no space in-between the arrow operator and the
|
283
|
+
parenthesis</p>
|
284
|
+
<pre><code># Both of this will not trigger an error,
|
285
|
+
# even with arrow_spacing enabled.
|
286
|
+
x(-> 3)
|
287
|
+
x( -> 3)
|
288
|
+
|
289
|
+
# However, this will trigger an error
|
290
|
+
x((a,b)-> 3)
|
291
|
+
</code>
|
292
|
+
</pre>
|
293
|
+
"""
|
131
294
|
|
132
295
|
coffeescript_error :
|
133
296
|
level : ERROR
|
@@ -265,8 +428,10 @@ class LineLinter
|
|
265
428
|
checkLineLength : () ->
|
266
429
|
rule = 'max_line_length'
|
267
430
|
max = @config[rule]?.value
|
268
|
-
if max and max < @line.length
|
269
|
-
|
431
|
+
if max and max < @line.length and not regexes.longUrlComment.test(@line)
|
432
|
+
attrs =
|
433
|
+
context: "Length is #{@line.length}, max is #{max}"
|
434
|
+
@createLineError(rule, attrs)
|
270
435
|
else
|
271
436
|
null
|
272
437
|
|
@@ -378,8 +543,7 @@ class LineLinter
|
|
378
543
|
null
|
379
544
|
|
380
545
|
#
|
381
|
-
# A class that performs checks on the output of CoffeeScript's
|
382
|
-
# lexer.
|
546
|
+
# A class that performs checks on the output of CoffeeScript's lexer.
|
383
547
|
#
|
384
548
|
class LexicalLinter
|
385
549
|
|
@@ -405,8 +569,7 @@ class LexicalLinter
|
|
405
569
|
errors.push(error) if error
|
406
570
|
errors
|
407
571
|
|
408
|
-
# Return an error if the given token fails a lint check, false
|
409
|
-
# otherwise.
|
572
|
+
# Return an error if the given token fails a lint check, false otherwise.
|
410
573
|
lintToken : (token) ->
|
411
574
|
[type, value, lineNumber] = token
|
412
575
|
|
@@ -437,17 +600,26 @@ class LexicalLinter
|
|
437
600
|
when "PARAM_START" then @lintParam(token)
|
438
601
|
when "@" then @lintStandaloneAt(token)
|
439
602
|
when "+", "-" then @lintPlus(token)
|
440
|
-
when "=", "MATH", "COMPARE", "LOGIC"
|
603
|
+
when "=", "MATH", "COMPARE", "LOGIC", "COMPOUND_ASSIGN"
|
441
604
|
@lintMath(token)
|
442
605
|
else null
|
443
606
|
|
444
607
|
lintUnary: (token) ->
|
445
608
|
if token[1] is 'new'
|
446
|
-
|
609
|
+
# Find the last chained identifier, e.g. Bar in new foo.bar.Bar().
|
610
|
+
identifierIndex = 1
|
611
|
+
loop
|
612
|
+
expectedIdentifier = @peek(identifierIndex)
|
613
|
+
expectedCallStart = @peek(identifierIndex + 1)
|
614
|
+
if expectedIdentifier?[0] is 'IDENTIFIER'
|
615
|
+
if expectedCallStart?[0] is '.'
|
616
|
+
identifierIndex += 2
|
617
|
+
continue
|
618
|
+
break
|
619
|
+
|
447
620
|
# The callStart is generated if your parameters are all on the same
|
448
621
|
# line with implicit parens, and if your parameters start on the
|
449
622
|
# next line, but is missing if there are no params and no parens.
|
450
|
-
expectedCallStart = @peek(2)
|
451
623
|
if expectedIdentifier?[0] is 'IDENTIFIER' and expectedCallStart?
|
452
624
|
if expectedCallStart[0] is 'CALL_START'
|
453
625
|
if expectedCallStart.generated
|
@@ -500,7 +672,8 @@ class LexicalLinter
|
|
500
672
|
p = @peek(-1)
|
501
673
|
unaries = ['TERMINATOR', '(', '=', '-', '+', ',', 'CALL_START',
|
502
674
|
'INDEX_START', '..', '...', 'COMPARE', 'IF',
|
503
|
-
'THROW', 'LOGIC', 'POST_IF', ':', '[', 'INDENT'
|
675
|
+
'THROW', 'LOGIC', 'POST_IF', ':', '[', 'INDENT',
|
676
|
+
'COMPOUND_ASSIGN', 'RETURN', 'MATH']
|
504
677
|
isUnary = if not p then false else p[0] in unaries
|
505
678
|
if (isUnary and token.spaced) or
|
506
679
|
(not isUnary and not token.spaced and not token.newLine)
|
@@ -602,7 +775,16 @@ class LexicalLinter
|
|
602
775
|
isIndexStart = nextToken[0] == 'INDEX_START'
|
603
776
|
isDot = nextToken[0] == '.'
|
604
777
|
|
605
|
-
|
778
|
+
# https://github.com/jashkenas/coffee-script/issues/1601
|
779
|
+
# @::foo is valid, but @:: behaves inconsistently and is planned for
|
780
|
+
# removal. Technically @:: is a stand alone ::, but I think it makes
|
781
|
+
# sense to group it into no_stand_alone_at
|
782
|
+
if nextToken[0] == '::'
|
783
|
+
protoProperty = @peek(2)
|
784
|
+
isValidProtoProperty = protoProperty[0] == 'IDENTIFIER'
|
785
|
+
|
786
|
+
if spaced or (not isIdentifier and not isIndexStart and
|
787
|
+
not isDot and not isValidProtoProperty)
|
606
788
|
@createLexError('no_stand_alone_at')
|
607
789
|
|
608
790
|
|
@@ -640,9 +822,13 @@ class LexicalLinter
|
|
640
822
|
# lines, which can be ignored.
|
641
823
|
if @isChainedCall()
|
642
824
|
currentLine = @lines[@lineNumber]
|
643
|
-
|
644
|
-
previousIndentation = previousLine.match(/^(\s*)/)[1].length
|
825
|
+
prevNum = 1
|
645
826
|
|
827
|
+
# keep going back until we are not at a comment or a blank line
|
828
|
+
prevNum += 1 while (/^\s*(#|$)/.test(@lines[@lineNumber - prevNum]))
|
829
|
+
previousLine = @lines[@lineNumber - prevNum]
|
830
|
+
|
831
|
+
previousIndentation = previousLine.match(/^(\s*)/)[1].length
|
646
832
|
# I don't know why, but when inside a function, you make a chained
|
647
833
|
# call and define an inline callback as a parameter, the body of
|
648
834
|
# that callback gets the indentation reported higher than it really
|
@@ -691,7 +877,7 @@ class LexicalLinter
|
|
691
877
|
|
692
878
|
lintArrowSpacing : (token) ->
|
693
879
|
# Throw error unless the following happens.
|
694
|
-
|
880
|
+
#
|
695
881
|
# We will take a look at the previous token to see
|
696
882
|
# 1. That the token is properly spaced
|
697
883
|
# 2. Wasn't generated by the CoffeeScript compiler
|
@@ -699,13 +885,19 @@ class LexicalLinter
|
|
699
885
|
# 4. If the function declaration has no parameters
|
700
886
|
# e.g. x(-> 3)
|
701
887
|
# x( -> 3)
|
888
|
+
#
|
889
|
+
# or a statement is wrapped in parentheses
|
890
|
+
# e.g. (-> true)()
|
891
|
+
#
|
702
892
|
# we will accept either having a space or not having a space there.
|
893
|
+
|
894
|
+
pp = @peek(-1)
|
703
895
|
unless (token.spaced? or token.newLine?) and
|
704
896
|
# Throw error unless the previous token...
|
705
|
-
(
|
706
|
-
|
707
|
-
|
708
|
-
(
|
897
|
+
((pp.spaced? or pp[0] is 'TERMINATOR') or #1
|
898
|
+
pp.generated? or #2
|
899
|
+
pp[0] is "INDENT" or #3
|
900
|
+
(pp[1] is "(" and not pp.generated?)) #4
|
709
901
|
@createLexError('arrow_spacing')
|
710
902
|
else
|
711
903
|
null
|
@@ -763,10 +955,8 @@ class ASTLinter
|
|
763
955
|
@lintNode(@node)
|
764
956
|
@errors
|
765
957
|
|
766
|
-
#
|
767
|
-
|
768
|
-
|
769
|
-
# Get the complexity of the current node.
|
958
|
+
# returns the "complexity" value of the current node.
|
959
|
+
getComplexity : (node) ->
|
770
960
|
name = node.constructor.name
|
771
961
|
complexity = if name in ['If', 'While', 'For', 'Try']
|
772
962
|
1
|
@@ -776,21 +966,30 @@ class ASTLinter
|
|
776
966
|
node.cases.length
|
777
967
|
else
|
778
968
|
0
|
969
|
+
return complexity
|
970
|
+
|
971
|
+
# Lint the AST node and return it's cyclomatic complexity.
|
972
|
+
lintNode : (node, line) ->
|
973
|
+
|
974
|
+
# Get the complexity of the current node.
|
975
|
+
name = node.constructor.name
|
976
|
+
complexity = @getComplexity(node)
|
779
977
|
|
780
978
|
# Add the complexity of all child's nodes to this one.
|
781
979
|
node.eachChild (childNode) =>
|
782
|
-
|
783
|
-
complexity += @lintNode(childNode)
|
784
|
-
return true
|
980
|
+
nodeLine = childNode.locationData.first_line
|
981
|
+
complexity += @lintNode(childNode, nodeLine) if childNode
|
785
982
|
|
786
983
|
# If the current node is a function, and it's over our limit, add an
|
787
984
|
# error to the list.
|
788
985
|
rule = @config.cyclomatic_complexity
|
986
|
+
|
789
987
|
if name == 'Code' and complexity >= rule.value
|
790
988
|
attrs = {
|
791
989
|
context: complexity + 1
|
792
990
|
level: rule.level
|
793
|
-
|
991
|
+
lineNumber: line + 1
|
992
|
+
lineNumberEnd: node.locationData.last_line + 1
|
794
993
|
}
|
795
994
|
error = createError 'cyclomatic_complexity', attrs
|
796
995
|
@errors.push error if error
|
@@ -826,6 +1025,22 @@ mergeDefaultConfig = (userConfig) ->
|
|
826
1025
|
config[rule] = defaults(userConfig[rule], ruleConfig)
|
827
1026
|
return config
|
828
1027
|
|
1028
|
+
coffeelint.invertLiterate = (source) ->
|
1029
|
+
source = CoffeeScript.helpers.invertLiterate source
|
1030
|
+
# Strip the first 4 spaces from every line. After this the markdown is
|
1031
|
+
# commented and all of the other code should be at their natural location.
|
1032
|
+
newSource = ""
|
1033
|
+
for line in source.split "\n"
|
1034
|
+
if line.match(/^#/)
|
1035
|
+
# strip trailing space
|
1036
|
+
line = line.replace /\s*$/, ''
|
1037
|
+
# Strip the first 4 spaces of every line. This is how Markdown
|
1038
|
+
# indicates code, so in the end this pulls everything back to where it
|
1039
|
+
# would be indented if it hadn't been written in literate style.
|
1040
|
+
line = line.replace /^\s{4}/g, ''
|
1041
|
+
newSource += "#{line}\n"
|
1042
|
+
|
1043
|
+
newSource
|
829
1044
|
|
830
1045
|
# Check the source against the given configuration and return an array
|
831
1046
|
# of any errors found. An error is an object with the following
|
@@ -839,14 +1054,16 @@ mergeDefaultConfig = (userConfig) ->
|
|
839
1054
|
# context: 'Optional details about why the rule was violated'
|
840
1055
|
# }
|
841
1056
|
#
|
842
|
-
coffeelint.lint = (source, userConfig = {}) ->
|
1057
|
+
coffeelint.lint = (source, userConfig = {}, literate = false) ->
|
1058
|
+
source = @invertLiterate source if literate
|
1059
|
+
|
843
1060
|
config = mergeDefaultConfig(userConfig)
|
844
1061
|
|
845
1062
|
# Check ahead for inline enabled rules
|
846
1063
|
disabled_initially = []
|
847
1064
|
for l in source.split('\n')
|
848
1065
|
s = regexes.configStatement.exec(l)
|
849
|
-
if s
|
1066
|
+
if s?.length > 2 and 'enable' in s
|
850
1067
|
for r in s[1..]
|
851
1068
|
unless r in ['enable','disable']
|
852
1069
|
unless r of config and config[r].level in ['warn','error']
|
data/lib/coffeelint.rb
CHANGED
@@ -9,51 +9,71 @@ module Coffeelint
|
|
9
9
|
@path ||= File.expand_path('../../coffeelint/src/coffeelint.coffee', __FILE__)
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.
|
12
|
+
def self.colorize(str, color_code)
|
13
|
+
"\e[#{color_code}m#{str}\e[0m"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.red(str, pretty_output = true)
|
17
|
+
pretty_output ? Coffeelint.colorize(str, 31) : str
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.green(str, pretty_output = true)
|
21
|
+
pretty_output ? Coffeelint.colorize(str, 32) : str
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.lint(script, config = {})
|
25
|
+
if !config[:config_file].nil?
|
26
|
+
fname = config.delete(:config_file)
|
27
|
+
config.merge!(JSON.parse(File.read(fname)))
|
28
|
+
end
|
13
29
|
coffeescriptSource = File.read(CoffeeScript::Source.path)
|
14
30
|
coffeelintSource = CoffeeScript.compile(File.read(Coffeelint.path))
|
15
31
|
context = ExecJS.compile(coffeescriptSource + coffeelintSource)
|
16
|
-
context.call('coffeelint.lint', script)
|
32
|
+
context.call('coffeelint.lint', script, config)
|
17
33
|
end
|
18
34
|
|
19
|
-
def self.lint_file(filename)
|
20
|
-
Coffeelint.lint(File.read(filename))
|
35
|
+
def self.lint_file(filename, config = {})
|
36
|
+
Coffeelint.lint(File.read(filename), config)
|
21
37
|
end
|
22
38
|
|
23
|
-
def self.lint_dir(directory)
|
39
|
+
def self.lint_dir(directory, config = {})
|
24
40
|
retval = {}
|
25
41
|
Dir.glob("#{directory}/**/*.coffee") do |name|
|
26
|
-
retval[name] = Coffeelint.lint_file(name)
|
42
|
+
retval[name] = Coffeelint.lint_file(name, config)
|
27
43
|
yield name, retval[name] if block_given?
|
28
44
|
end
|
29
45
|
retval
|
30
46
|
end
|
31
47
|
|
32
|
-
def self.display_test_results(name, errors)
|
33
|
-
good = "\u2713"
|
34
|
-
bad = "\u2717"
|
48
|
+
def self.display_test_results(name, errors, pretty_output = true)
|
49
|
+
good = pretty_output ? "\u2713" : 'Passed'
|
50
|
+
bad = pretty_output ? "\u2717" : 'Failed'
|
35
51
|
|
36
52
|
if errors.length == 0
|
37
|
-
puts " #{good}
|
53
|
+
puts " #{good} " + Coffeelint.green(name, pretty_output)
|
38
54
|
return true
|
39
55
|
else
|
40
|
-
puts " #{bad}
|
56
|
+
puts " #{bad} " + Coffeelint.red(name, pretty_output)
|
41
57
|
errors.each do |error|
|
42
|
-
|
58
|
+
print " #{bad} "
|
59
|
+
print Coffeelint.red(error["lineNumber"], pretty_output)
|
60
|
+
puts ": #{error["message"]}, #{error["context"]}."
|
43
61
|
end
|
44
62
|
return false
|
45
63
|
end
|
46
64
|
end
|
47
65
|
|
48
|
-
def self.run_test(file)
|
49
|
-
|
50
|
-
Coffeelint.
|
66
|
+
def self.run_test(file, config = {})
|
67
|
+
pretty_output = config.has_key?(:pretty_output) ? config.delete(:pretty_output) : true
|
68
|
+
result = Coffeelint.lint_file(file, config)
|
69
|
+
Coffeelint.display_test_results(file, result, pretty_output)
|
51
70
|
end
|
52
71
|
|
53
|
-
def self.run_test_suite(directory)
|
72
|
+
def self.run_test_suite(directory, config = {})
|
73
|
+
pretty_output = config.has_key?(:pretty_output) ? config.delete(:pretty_output) : true
|
54
74
|
success = true
|
55
|
-
Coffeelint.lint_dir(directory) do |name, errors|
|
56
|
-
result = Coffeelint.display_test_results(name, errors)
|
75
|
+
Coffeelint.lint_dir(directory, config) do |name, errors|
|
76
|
+
result = Coffeelint.display_test_results(name, errors, pretty_output)
|
57
77
|
success = false if not result
|
58
78
|
end
|
59
79
|
success
|
data/lib/coffeelint/version.rb
CHANGED
data/spec/coffeelint_spec.rb
CHANGED
@@ -7,4 +7,15 @@ describe Coffeelint do
|
|
7
7
|
result = results[0]
|
8
8
|
result['message'].should include 'trailing semicolon'
|
9
9
|
end
|
10
|
+
|
11
|
+
it 'should be able to disable a linter' do
|
12
|
+
results = Coffeelint.lint('apple;', :no_trailing_semicolons => { :level => "ignore" } )
|
13
|
+
results.length.should == 0
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should be able to take a config file in the parameters' do
|
17
|
+
File.open('/tmp/coffeelint.json', 'w') {|f| f.write(JSON.dump({:no_trailing_semicolons => { :level => "ignore" }})) }
|
18
|
+
results = Coffeelint.lint('apple;', :config_file => "/tmp/coffeelint.json")
|
19
|
+
results.length.should == 0
|
20
|
+
end
|
10
21
|
end
|
metadata
CHANGED
@@ -1,65 +1,53 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: coffeelint
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
|
-
authors:
|
6
|
+
authors:
|
7
7
|
- Zachary Bush
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
|
12
|
+
date: 2013-10-20 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
14
15
|
name: coffee-script
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ! '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
16
|
prerelease: false
|
22
|
-
|
23
|
-
requirements:
|
24
|
-
-
|
25
|
-
-
|
26
|
-
|
27
|
-
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- &id002
|
20
|
+
- ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
type: :runtime
|
24
|
+
version_requirements: *id001
|
25
|
+
- !ruby/object:Gem::Dependency
|
28
26
|
name: rspec
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ! '>='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
27
|
prerelease: false
|
36
|
-
|
37
|
-
requirements:
|
38
|
-
-
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ! '>='
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
28
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- *id002
|
48
31
|
type: :development
|
32
|
+
version_requirements: *id003
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
49
35
|
prerelease: false
|
50
|
-
|
51
|
-
requirements:
|
52
|
-
-
|
53
|
-
|
54
|
-
|
36
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- *id002
|
39
|
+
type: :development
|
40
|
+
version_requirements: *id004
|
55
41
|
description: Ruby bindings for coffeelint
|
56
|
-
email:
|
42
|
+
email:
|
57
43
|
- zach@zmbush.com
|
58
|
-
executables:
|
44
|
+
executables:
|
59
45
|
- coffeelint.rb
|
60
46
|
extensions: []
|
47
|
+
|
61
48
|
extra_rdoc_files: []
|
62
|
-
|
49
|
+
|
50
|
+
files:
|
63
51
|
- .gitignore
|
64
52
|
- .gitmodules
|
65
53
|
- Gemfile
|
@@ -76,29 +64,28 @@ files:
|
|
76
64
|
- spec/spec_helper.rb
|
77
65
|
- coffeelint/src/coffeelint.coffee
|
78
66
|
homepage: https://github.com/zipcodeman/coffeelint-ruby
|
79
|
-
licenses:
|
67
|
+
licenses:
|
80
68
|
- MIT
|
81
69
|
metadata: {}
|
70
|
+
|
82
71
|
post_install_message:
|
83
72
|
rdoc_options: []
|
84
|
-
|
73
|
+
|
74
|
+
require_paths:
|
85
75
|
- lib
|
86
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
requirements:
|
93
|
-
- - ! '>='
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
version: '0'
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- *id002
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- *id002
|
96
82
|
requirements: []
|
83
|
+
|
97
84
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.
|
85
|
+
rubygems_version: 2.1.9
|
99
86
|
signing_key:
|
100
87
|
specification_version: 4
|
101
88
|
summary: Ruby bindings for coffeelint along with railtie to add rake task to rails
|
102
|
-
test_files:
|
89
|
+
test_files:
|
103
90
|
- spec/coffeelint_spec.rb
|
104
91
|
- spec/spec_helper.rb
|