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 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