scss-lint 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/scss_lint.rb +5 -3
  3. data/lib/scss_lint/cli.rb +9 -2
  4. data/lib/scss_lint/constants.rb +5 -0
  5. data/lib/scss_lint/linter.rb +11 -0
  6. data/lib/scss_lint/linter/border_zero.rb +8 -7
  7. data/lib/scss_lint/linter/capitalization_in_selector.rb +1 -0
  8. data/lib/scss_lint/linter/color_keyword.rb +2 -0
  9. data/lib/scss_lint/linter/comment.rb +1 -0
  10. data/lib/scss_lint/linter/debug_statement.rb +1 -0
  11. data/lib/scss_lint/linter/declaration_order.rb +1 -0
  12. data/lib/scss_lint/linter/declared_name.rb +2 -0
  13. data/lib/scss_lint/linter/id_with_extraneous_selector.rb +1 -0
  14. data/lib/scss_lint/linter/indentation.rb +1 -0
  15. data/lib/scss_lint/linter/leading_zero.rb +1 -0
  16. data/lib/scss_lint/linter/placeholder_in_extend.rb +1 -0
  17. data/lib/scss_lint/linter/shorthand.rb +2 -0
  18. data/lib/scss_lint/linter/single_line_per_selector.rb +1 -0
  19. data/lib/scss_lint/linter/sorted_properties.rb +1 -0
  20. data/lib/scss_lint/linter/space_after_comma.rb +87 -0
  21. data/lib/scss_lint/linter/space_after_property_colon.rb +39 -0
  22. data/lib/scss_lint/linter/space_after_property_name.rb +13 -0
  23. data/lib/scss_lint/linter/space_before_brace.rb +1 -0
  24. data/lib/scss_lint/linter/trailing_semicolon_after_property_value.rb +29 -0
  25. data/lib/scss_lint/linter/zero_unit.rb +1 -0
  26. data/lib/scss_lint/runner.rb +18 -7
  27. data/lib/{sass → scss_lint/sass}/script.rb +0 -0
  28. data/lib/{sass → scss_lint/sass}/tree.rb +0 -0
  29. data/lib/scss_lint/utils.rb +5 -0
  30. data/lib/scss_lint/version.rb +1 -1
  31. metadata +10 -6
  32. data/lib/scss_lint/linter/property_format.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 62980ca32775a70ec7170fc8b7399eb13b304c7b
4
- data.tar.gz: 76fa2ea621edad2ad9ebc9d0e2b302b57c43a70d
3
+ metadata.gz: 5efc8809d431b7de019112e2771839116d88df95
4
+ data.tar.gz: d54b6ba942585c4fbdc969c389af86e0da47088c
5
5
  SHA512:
6
- metadata.gz: ce7dfa08c1c68044da63de6f376a3ce483e5145eaa7854668b616dbd0b1596c983e7dbc5f510c8cdbf3d27f47658b21cebe5f0caa63b9c9a888ef0d0df772729
7
- data.tar.gz: dae5b62b186b24c851394b7e18c16e45f4f1281a2046c7f6a00513deb94d8300df30e8a5e7e1d3845c77f9efe68f1fef5c9e4f059239499b55e56cf4ff24f0c4
6
+ metadata.gz: 406a4cd62baa39e5dc561c5cd84cedc7ef7b4d64a0b8813a693c22fb3192640f0382b3a839f317fdbe0f75738c6ed53b3f494bd5e9c32ed50f79833841e76f43
7
+ data.tar.gz: e0c8b74d3cf6bb80f592cfacc9ed25fe642b397e36ca9e965ee31a4889a8291ea2b88334aba45aae0c4eaf5cf52c610150bf6bc0b2ecd5b6af92aa4547ea21b5
@@ -10,12 +10,14 @@ module SCSSLint
10
10
  autoload :Runner, 'scss_lint/runner'
11
11
  autoload :SelectorVisitor, 'scss_lint/selector_visitor'
12
12
  autoload :Utils, 'scss_lint/utils'
13
- autoload :VERSION, 'scss_lint/version'
13
+
14
+ require 'scss_lint/constants'
15
+ require 'scss_lint/version'
14
16
 
15
17
  # Preload Sass so we can monkey patch it
16
18
  require 'sass'
17
- require File.expand_path('sass/script', File.dirname(__FILE__))
18
- require File.expand_path('sass/tree', File.dirname(__FILE__))
19
+ require File.expand_path('scss_lint/sass/script', File.dirname(__FILE__))
20
+ require File.expand_path('scss_lint/sass/tree', File.dirname(__FILE__))
19
21
 
20
22
  # Load all linters
21
23
  Dir[File.expand_path('scss_lint/linter/*.rb', File.dirname(__FILE__))].each do |file|
@@ -1,6 +1,8 @@
1
1
  require 'optparse'
2
2
 
3
3
  module SCSSLint
4
+ # Responsible for parsing command-line options and executing the appropriate
5
+ # application logic based on the options specified.
4
6
  class CLI
5
7
  attr_accessor :options
6
8
 
@@ -62,10 +64,15 @@ module SCSSLint
62
64
  runner = Runner.new(options)
63
65
  runner.run(find_files)
64
66
  report_lints(runner.lints)
65
- halt 1 if runner.lints?
67
+ halt(1) if runner.lints?
66
68
  rescue NoFilesError, NoSuchLinter, Errno::ENOENT => ex
67
69
  puts ex.message
68
- halt -1
70
+ halt(-1)
71
+ rescue => ex
72
+ puts ex.message
73
+ puts ex.backtrace
74
+ puts 'Report this bug at '.yellow + BUG_REPORT_URL.cyan
75
+ halt(-1)
69
76
  end
70
77
 
71
78
  private
@@ -0,0 +1,5 @@
1
+ # Global application constants.
2
+ module SCSSLint
3
+ REPO_URL = 'https://github.com/causes/scss-lint'
4
+ BUG_REPORT_URL = "#{REPO_URL}/issues"
5
+ end
@@ -28,6 +28,17 @@ module SCSSLint
28
28
  message || description)
29
29
  end
30
30
 
31
+ # Returns the character at the given [Sass::Source::Position]
32
+ def character_at(source_position, offset = 0)
33
+ actual_line = source_position.line - 1
34
+ actual_offset = source_position.offset + offset - 1
35
+
36
+ # Return a newline if offset points at the very end of the line
37
+ return "\n" if actual_offset == engine.lines[actual_line].length
38
+
39
+ engine.lines[actual_line][actual_offset]
40
+ end
41
+
31
42
  # Monkey-patched implementation that adds support for traversing
32
43
  # Sass::Script::Nodes (original implementation only supports
33
44
  # Sass::Tree::Nodes).
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Reports when `border: 0` can be used instead of `border: none`.
2
3
  class Linter::BorderZero < Linter
3
4
  include LinterRegistry
4
5
 
@@ -10,13 +11,13 @@ module SCSSLint
10
11
  def description
11
12
  '`border: 0;` is preferred over `border: none;`'
12
13
  end
13
- end
14
14
 
15
- private
15
+ private
16
16
 
17
- BORDER_PROPERTIES = %w[border
18
- border-top
19
- border-right
20
- border-bottom
21
- border-left]
17
+ BORDER_PROPERTIES = %w[border
18
+ border-top
19
+ border-right
20
+ border-bottom
21
+ border-left]
22
+ end
22
23
  end
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for capitalized letters in IDs, classes, types, etc. in selectors.
2
3
  class Linter::CapitalizationInSelector < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,6 @@
1
1
  module SCSSLint
2
+ # Checks for uses of a color keyword instead of the preferred hexadecimal
3
+ # form.
2
4
  class Linter::ColorKeyword < Linter
3
5
  include LinterRegistry
4
6
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for uses of renderable comments (/* ... */)
2
3
  class Linter::Comment < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for leftover `@debug` statements.
2
3
  class Linter::DebugStatement < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks the order of nested items within a rule set.
2
3
  class Linter::DeclarationOrder < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,6 @@
1
1
  module SCSSLint
2
+ # Checks that the declared names of functions, mixins, and variables are all
3
+ # lowercase and use hyphens instead of underscores.
2
4
  class Linter::DeclaredName < Linter
3
5
  include LinterRegistry
4
6
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for a selector with an ID combined with some other selector.
2
3
  class Linter::IdWithExtraneousSelector < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for consistent indentation of nested declarations and rule sets.
2
3
  class Linter::Indentation < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for unnecessary leading zeros in numeric values with decimal points.
2
3
  class Linter::LeadingZero < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks that `@extend` is always used with a placeholder selector.
2
3
  class Linter::PlaceholderInExtend < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,6 @@
1
1
  module SCSSLint
2
+ # Checks for the use of the shortest form for properties that can be written
3
+ # in shorthand.
2
4
  class Linter::Shorthand < Linter
3
5
  include LinterRegistry
4
6
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks that selector sequences are split over multiple lines by comma.
2
3
  class Linter::SingleLinePerSelector < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks the declaration order of properties.
2
3
  class Linter::SortedProperties < Linter
3
4
  include LinterRegistry
4
5
 
@@ -0,0 +1,87 @@
1
+ module SCSSLint
2
+ # Checks for spaces after commas in argument lists.
3
+ class Linter::SpaceAfterComma < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_mixindef(node)
7
+ check_definition(node, :mixin)
8
+ yield
9
+ end
10
+
11
+ def visit_mixin(node)
12
+ check_invocation(node, :mixin)
13
+ yield
14
+ end
15
+
16
+ def visit_function(node)
17
+ check_definition(node, :function)
18
+ yield
19
+ end
20
+
21
+ def visit_script_funcall(node)
22
+ check_invocation(node, :function)
23
+ yield
24
+ end
25
+
26
+ def visit_script_listliteral(node)
27
+ check_commas_after_args(node.elements, 'lists') if node.separator == :comma
28
+ yield
29
+ end
30
+
31
+ private
32
+
33
+ # Check parameters of a function/mixin definition
34
+ def check_definition(node, type)
35
+ # Use the default value's source range if one is defined, since that will
36
+ # be the item with the comma after it
37
+ args = node.args.map { |name, default_value| default_value || name }
38
+ args << node.splat if node.splat
39
+
40
+ check_commas_after_args(args, "#{type} parameters")
41
+ end
42
+
43
+ # Check arguments passed to a function/mixin invocation
44
+ def check_invocation(node, type)
45
+ args = sort_args_by_position(node.args,
46
+ node.splat,
47
+ node.keywords.values,
48
+ node.kwarg_splat)
49
+
50
+ check_commas_after_args(args, "#{type} arguments")
51
+ end
52
+
53
+ # Since keyword arguments are not guaranteed to be in order, use the source
54
+ # range to order arguments so we check them in the order they were declared.
55
+ def sort_args_by_position(*args)
56
+ args.flatten.compact.sort_by do |arg|
57
+ pos = arg.source_range.end_pos
58
+ [pos.line, pos.offset]
59
+ end
60
+ end
61
+
62
+ EXPECTED_SPACES_AFTER_COMMA = 1
63
+
64
+ # Check the comma after each argument in a list for a space following it,
65
+ # reporting a lint using the given [arg_type].
66
+ def check_commas_after_args(args, arg_type)
67
+ # For each arg except the last, check the character following the comma
68
+ args[0..-2].each do |arg|
69
+ # Find the first comma following the arg
70
+ offset = 1
71
+ offset += 1 while character_at(arg.source_range.end_pos, offset - 1) != ','
72
+
73
+ # Check for space or newline after arg (we allow arguments to be split
74
+ # up over multiple lines).
75
+ spaces = 0
76
+ while character_at(arg.source_range.end_pos, offset) =~ / |\n/
77
+ spaces += 1
78
+ offset += 1
79
+ end
80
+
81
+ if spaces != EXPECTED_SPACES_AFTER_COMMA
82
+ add_lint arg, 'Commas in %s should be followed by a single space' % arg_type
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,39 @@
1
+ module SCSSLint
2
+ # Checks for spaces following the colon that separates a property's name from
3
+ # its value.
4
+ class Linter::SpaceAfterPropertyColon < Linter
5
+ include LinterRegistry
6
+
7
+ EXPECTED_SPACES_AFTER_COLON = 1
8
+
9
+ def visit_prop(node)
10
+ spaces = spaces_after_colon(node)
11
+
12
+ if spaces != EXPECTED_SPACES_AFTER_COLON
13
+ add_lint node, 'Colon after property should be followed by ' <<
14
+ "#{pluralize(EXPECTED_SPACES_AFTER_COLON, 'space')} instead of " <<
15
+ pluralize(spaces, 'space')
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def spaces_after_colon(node)
22
+ spaces = 0
23
+ offset = 1
24
+
25
+ # Handle quirk where Sass parser doesn't include colon in source range
26
+ # when property name is followed by spaces
27
+ if character_at(node.name_source_range.end_pos, offset) == ':'
28
+ offset += 1
29
+ end
30
+
31
+ while character_at(node.name_source_range.end_pos, offset) == ' '
32
+ spaces += 1
33
+ offset += 1
34
+ end
35
+
36
+ spaces
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ module SCSSLint
2
+ # Checks for spaces following the name of a property and before the colon
3
+ # separating the property's name from its value.
4
+ class Linter::SpaceAfterPropertyName < Linter
5
+ include LinterRegistry
6
+
7
+ def visit_prop(node)
8
+ if character_at(node.name_source_range.end_pos) != ':'
9
+ add_lint node, 'Property name should be immediately followed by a colon'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for the presence of a single space before an opening brace.
2
3
  class Linter::SpaceBeforeBrace < Linter
3
4
  include LinterRegistry
4
5
 
@@ -0,0 +1,29 @@
1
+ module SCSSLint
2
+ # Checks properties for a trailing semicolon (unless that property is a
3
+ # namespace which has nested properties).
4
+ class Linter::TrailingSemicolonAfterPropertyValue < Linter
5
+ include LinterRegistry
6
+
7
+ def visit_prop(node)
8
+ has_nested_props = has_nested_properties?(node)
9
+
10
+ if !has_nested_props && !ends_with_semicolon?(node)
11
+ add_lint node.line, 'Property declaration should end with a semicolon'
12
+ end
13
+
14
+ yield if has_nested_props
15
+ end
16
+
17
+ private
18
+
19
+ def has_nested_properties?(node)
20
+ node.children.any? { |n| n.is_a?(Sass::Tree::PropNode) }
21
+ end
22
+
23
+ # Checks that the property is ended by a semicolon (with no whitespace)
24
+ def ends_with_semicolon?(node)
25
+ character_at(node.source_range.end_pos) == ';' &&
26
+ character_at(node.source_range.end_pos, -1) !~ /\s/
27
+ end
28
+ end
29
+ end
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for unnecessary units on zero values.
2
3
  class Linter::ZeroUnit < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,7 +1,10 @@
1
1
  module SCSSLint
2
+ class LinterError < StandardError; end
2
3
  class NoFilesError < StandardError; end
3
4
  class NoLintersError < StandardError; end
4
5
 
6
+ # Finds and aggregates all lints found by running the registered linters
7
+ # against a set of SCSS files.
5
8
  class Runner
6
9
  attr_reader :linters, :lints
7
10
 
@@ -20,8 +23,8 @@ module SCSSLint
20
23
  end
21
24
 
22
25
  def run(files = [])
23
- raise NoFilesError.new('No SCSS files specified') if files.empty?
24
- raise NoLintersError.new('No linters specified') if linters.empty?
26
+ raise NoFilesError, 'No SCSS files specified' if files.empty?
27
+ raise NoLintersError, 'No linters specified' if linters.empty?
25
28
 
26
29
  files.each do |file|
27
30
  find_lints(file)
@@ -32,18 +35,26 @@ module SCSSLint
32
35
  end
33
36
  end
34
37
 
38
+ def lints?
39
+ lints.any?
40
+ end
41
+
42
+ private
43
+
35
44
  def find_lints(file)
36
45
  engine = Engine.new(file)
37
46
 
38
47
  linters.each do |linter|
39
- linter.run(engine)
48
+ begin
49
+ linter.run(engine)
50
+ rescue => error
51
+ raise LinterError,
52
+ "#{linter.class} raised unexpected error: '#{error.message}'",
53
+ error.backtrace
54
+ end
40
55
  end
41
56
  rescue Sass::SyntaxError => ex
42
57
  @lints << Lint.new(ex.sass_filename, ex.sass_line, ex.to_s, :error)
43
58
  end
44
-
45
- def lints?
46
- lints.any?
47
- end
48
59
  end
49
60
  end
File without changes
File without changes
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Collection of helpers used across a variety of linters.
2
3
  module Utils
3
4
  # Given a selector array which is a list of strings with Sass::Script::Nodes
4
5
  # interspersed within them, return an array of strings representing those
@@ -55,6 +56,10 @@ module SCSSLint
55
56
  end
56
57
  end
57
58
 
59
+ def pluralize(value, word)
60
+ value == 1 ? "#{value} #{word}" : "#{value} #{word}s"
61
+ end
62
+
58
63
  private
59
64
 
60
65
  INVALID_NAME_CHARS = '[_A-Z]'
@@ -1,3 +1,3 @@
1
1
  module SCSSLint
2
- VERSION = '0.11.1'
2
+ VERSION = '0.12.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scss-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Causes Engineering
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-21 00:00:00.000000000 Z
12
+ date: 2013-11-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize
@@ -77,6 +77,7 @@ extensions: []
77
77
  extra_rdoc_files: []
78
78
  files:
79
79
  - lib/scss_lint/version.rb
80
+ - lib/scss_lint/constants.rb
80
81
  - lib/scss_lint/utils.rb
81
82
  - lib/scss_lint/reporter/xml_reporter.rb
82
83
  - lib/scss_lint/reporter/default_reporter.rb
@@ -85,19 +86,24 @@ files:
85
86
  - lib/scss_lint/lint.rb
86
87
  - lib/scss_lint/linter.rb
87
88
  - lib/scss_lint/selector_visitor.rb
88
- - lib/scss_lint/linter/property_format.rb
89
+ - lib/scss_lint/sass/tree.rb
90
+ - lib/scss_lint/sass/script.rb
91
+ - lib/scss_lint/linter/space_after_comma.rb
89
92
  - lib/scss_lint/linter/space_before_brace.rb
90
93
  - lib/scss_lint/linter/capitalization_in_selector.rb
91
94
  - lib/scss_lint/linter/indentation.rb
92
95
  - lib/scss_lint/linter/declared_name.rb
93
96
  - lib/scss_lint/linter/sorted_properties.rb
97
+ - lib/scss_lint/linter/trailing_semicolon_after_property_value.rb
94
98
  - lib/scss_lint/linter/shorthand.rb
95
99
  - lib/scss_lint/linter/id_with_extraneous_selector.rb
96
100
  - lib/scss_lint/linter/declaration_order.rb
97
101
  - lib/scss_lint/linter/zero_unit.rb
98
102
  - lib/scss_lint/linter/placeholder_in_extend.rb
103
+ - lib/scss_lint/linter/space_after_property_name.rb
99
104
  - lib/scss_lint/linter/usage_name.rb
100
105
  - lib/scss_lint/linter/border_zero.rb
106
+ - lib/scss_lint/linter/space_after_property_colon.rb
101
107
  - lib/scss_lint/linter/debug_statement.rb
102
108
  - lib/scss_lint/linter/duplicate_property.rb
103
109
  - lib/scss_lint/linter/hex_format.rb
@@ -109,11 +115,9 @@ files:
109
115
  - lib/scss_lint/cli.rb
110
116
  - lib/scss_lint/engine.rb
111
117
  - lib/scss_lint/linter_registry.rb
112
- - lib/sass/tree.rb
113
- - lib/sass/script.rb
114
118
  - lib/scss_lint.rb
115
119
  - bin/scss-lint
116
- homepage: http://github.com/causes/scss-lint
120
+ homepage: https://github.com/causes/scss-lint
117
121
  licenses:
118
122
  - MIT
119
123
  metadata: {}
@@ -1,29 +0,0 @@
1
- module SCSSLint
2
- class Linter::PropertyFormat < Linter
3
- include LinterRegistry
4
-
5
- def visit_prop(node)
6
- line = engine.lines[node.line - 1] if node.line
7
-
8
- add_lint(node) unless line =~ PROPERTY_RE
9
- end
10
-
11
- def description
12
- 'Property declarations should always be on one line of the form ' <<
13
- '`name: value;` or `name: [value] {` ' <<
14
- '(are you missing a trailing semi-colon?)'
15
- end
16
-
17
- private
18
-
19
- VALUE_RE = %r{(\S+\s)*\S+} # eg. "10px", "10px normal"
20
- PROPERTY_RE = %r{
21
- ^\s*[^:]+(?<!\s):\s # property name, colon not preceded by a space, one space
22
- ( # followed by
23
- #{VALUE_RE}; # property and terminating semi-colon eg. a b c;
24
- | # or
25
- ((#{VALUE_RE}\s)?\{) # nested property, optional value, trailing curly
26
- )
27
- }x
28
- end
29
- end