coffeelint 0.0.6 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NWNhOWRlOGVkMDhkMTQwMjFkMTRhYjMwZTQxMTE2MGQxOTkxZjFlMA==
5
- data.tar.gz: !binary |-
6
- NTVlMzA1ZTk2OGU5NDNiY2U2YTU3OTc0MTEyMzcyYTAwZDRhZTMxMQ==
7
- !binary "U0hBNTEy":
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
@@ -1,3 +1,3 @@
1
1
  [submodule "coffeelint"]
2
2
  path = coffeelint
3
- url = git://github.com/zipcodeman/coffeelint.git
3
+ url = git://github.com/clutchski/coffeelint.git
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 = {:recursive => false}
7
- OptionParser.new do |opts|
8
- opts.banner = "Usage: coffeelint [options] source [...]"
7
+ options = {
8
+ :recursive => false,
9
+ :noconfig => false,
10
+ :stdin => false,
11
+ :quiet => false,
12
+ }
9
13
 
10
- opts.on '-r', "Recursively lint .coffee files in subdirectories." do |f|
11
- options[:recursive] = f
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
- ARGV.each do |file|
16
- if options[:recursive]
17
- Coffeelint.run_test_suite(file)
18
- else
19
- Coffeelint.run_test(file)
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.4"
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 = () -&gt;
238
+
239
+ # We might favor this instead:
240
+ myFunction = -&gt;
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
- @createLineError(rule) unless regexes.longUrlComment.test(@line)
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
- expectedIdentifier = @peek(1)
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
- if spaced or (not isIdentifier and not isIndexStart and not isDot)
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
- previousLine = @lines[@lineNumber - 1]
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
- (@peek(-1).spaced? or #1
706
- @peek(-1).generated? or #2
707
- @peek(-1)[0] is "INDENT" or #3
708
- (@peek(-1)[0] is "CALL_START" and not @peek(-1).generated?)) #4
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
- # Lint the AST node and return it's cyclomatic complexity.
767
- lintNode : (node) ->
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
- return false unless childNode
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
- line: 0
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? and s.length > 2 and 'enable' in 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.lint(script)
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} \e[1m\e[32m#{name}\e[0m"
53
+ puts " #{good} " + Coffeelint.green(name, pretty_output)
38
54
  return true
39
55
  else
40
- puts " #{bad} \e[1m\e[31m#{name}\e[0m"
56
+ puts " #{bad} " + Coffeelint.red(name, pretty_output)
41
57
  errors.each do |error|
42
- puts " #{bad} \e[31m##{error["lineNumber"]}\e[0m: #{error["message"]}, #{error["context"]}."
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
- result = Coffeelint.lint_file(file)
50
- Coffeelint.display_test_results(file, result)
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
@@ -1,3 +1,3 @@
1
1
  module Coffeelint
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -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.0.6
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
- date: 2013-05-10 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
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
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ! '>='
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
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
- version_requirements: !ruby/object:Gem::Requirement
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
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
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
- files:
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
- require_paths:
73
+
74
+ require_paths:
85
75
  - lib
86
- required_ruby_version: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - ! '>='
89
- - !ruby/object:Gem::Version
90
- version: '0'
91
- required_rubygems_version: !ruby/object:Gem::Requirement
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.0.3
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