scss-lint 0.11.1 → 0.12.0

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