scss-lint 0.25.1 → 0.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +10 -1
  3. data/data/property-sort-orders/concentric.txt +99 -0
  4. data/lib/scss_lint.rb +1 -0
  5. data/lib/scss_lint/cli.rb +9 -3
  6. data/lib/scss_lint/exceptions.rb +4 -0
  7. data/lib/scss_lint/linter.rb +10 -1
  8. data/lib/scss_lint/linter/capitalization_in_selector.rb +14 -6
  9. data/lib/scss_lint/linter/compass/property_with_mixin.rb +9 -2
  10. data/lib/scss_lint/linter/indentation.rb +28 -6
  11. data/lib/scss_lint/linter/property_sort_order.rb +61 -9
  12. data/lib/scss_lint/linter/single_line_per_property.rb +53 -0
  13. data/lib/scss_lint/linter/single_line_per_selector.rb +6 -1
  14. data/lib/scss_lint/linter/space_after_comma.rb +27 -19
  15. data/lib/scss_lint/linter/space_before_brace.rb +5 -4
  16. data/lib/scss_lint/linter/trailing_semicolon.rb +53 -0
  17. data/lib/scss_lint/linter/unnecessary_parent_reference.rb +36 -0
  18. data/lib/scss_lint/reporter/default_reporter.rb +7 -2
  19. data/lib/scss_lint/reporter/xml_reporter.rb +2 -1
  20. data/lib/scss_lint/runner.rb +7 -3
  21. data/lib/scss_lint/version.rb +1 -1
  22. data/spec/scss_lint/cli_spec.rb +314 -0
  23. data/spec/scss_lint/config_spec.rb +439 -0
  24. data/spec/scss_lint/engine_spec.rb +24 -0
  25. data/spec/scss_lint/linter/border_zero_spec.rb +84 -0
  26. data/spec/scss_lint/linter/capitalization_in_selector_spec.rb +71 -0
  27. data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
  28. data/spec/scss_lint/linter/comment_spec.rb +55 -0
  29. data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
  30. data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
  31. data/spec/scss_lint/linter/declaration_order_spec.rb +94 -0
  32. data/spec/scss_lint/linter/duplicate_property_spec.rb +176 -0
  33. data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
  34. data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +263 -0
  35. data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
  36. data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
  37. data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
  38. data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
  39. data/spec/scss_lint/linter/hex_validation_spec.rb +36 -0
  40. data/spec/scss_lint/linter/id_with_extraneous_selector_spec.rb +139 -0
  41. data/spec/scss_lint/linter/indentation_spec.rb +242 -0
  42. data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
  43. data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
  44. data/spec/scss_lint/linter/name_format_spec.rb +206 -0
  45. data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
  46. data/spec/scss_lint/linter/property_sort_order_spec.rb +246 -0
  47. data/spec/scss_lint/linter/property_spelling_spec.rb +57 -0
  48. data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
  49. data/spec/scss_lint/linter/shorthand_spec.rb +172 -0
  50. data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
  51. data/spec/scss_lint/linter/single_line_per_selector_spec.rb +121 -0
  52. data/spec/scss_lint/linter/space_after_comma_spec.rb +315 -0
  53. data/spec/scss_lint/linter/space_after_property_colon_spec.rb +238 -0
  54. data/spec/scss_lint/linter/space_after_property_name_spec.rb +23 -0
  55. data/spec/scss_lint/linter/space_before_brace_spec.rb +447 -0
  56. data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
  57. data/spec/scss_lint/linter/string_quotes_spec.rb +303 -0
  58. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +188 -0
  59. data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
  60. data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +67 -0
  61. data/spec/scss_lint/linter/url_format_spec.rb +55 -0
  62. data/spec/scss_lint/linter/url_quotes_spec.rb +63 -0
  63. data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
  64. data/spec/scss_lint/linter_registry_spec.rb +50 -0
  65. data/spec/scss_lint/location_spec.rb +42 -0
  66. data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
  67. data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
  68. data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
  69. data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
  70. data/spec/scss_lint/reporter_spec.rb +11 -0
  71. data/spec/scss_lint/runner_spec.rb +132 -0
  72. data/spec/scss_lint/selector_visitor_spec.rb +264 -0
  73. data/spec/spec_helper.rb +34 -0
  74. data/spec/support/isolated_environment.rb +25 -0
  75. data/spec/support/matchers/report_lint.rb +48 -0
  76. metadata +126 -8
  77. data/lib/scss_lint/linter/trailing_semicolon_after_property_value.rb +0 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4383a0ff6df66ae43c6b5a3a7b83fff68515a414
4
- data.tar.gz: c4dd715631578ccba0f49bc2df013bd24d8c2488
3
+ metadata.gz: e07cf784e2e32fc05bf8294608db2d3bcf2951b2
4
+ data.tar.gz: d1577b62811661ad24efa01dfeeffc6ca19a573b
5
5
  SHA512:
6
- metadata.gz: 4fe2aaf47a16df5179c6a15d6559d45ed756f5d86eb90cbc8b87822c930fb2369b705f84c4faa9b5e972e0418ff2b843533fca108737e3c15dc9a73bd23adad9
7
- data.tar.gz: 095e7768edce611e4d91c9444d5db04fc9bec4a3c39489ef44a2809affeff4296e7eb7f5d5bab85d1a234da602fd3db442f44d6bc7294e2091b0721fba31eae2
6
+ metadata.gz: a3179febbb5fce02dee1044be6ea7b05f312e833e8bb61d4c8cb23f9ab84eb07c9458f9492f6918a2766a8c42e10ae89debd3a20959ca7778bc38787eccc96e2
7
+ data.tar.gz: e50715f6dbbf827a29b1f079d520ec6011e8318f3b89ea9d7d31afb09c8bf68a32f3cea66ab7a8da926ab2556c4a34fb3023a8393ae10aea89ef3889e5a34f19
@@ -52,6 +52,7 @@ linters:
52
52
 
53
53
  Indentation:
54
54
  enabled: true
55
+ character: space # or 'tab'
55
56
  width: 2
56
57
 
57
58
  LeadingZero:
@@ -71,6 +72,7 @@ linters:
71
72
 
72
73
  PropertySortOrder:
73
74
  enabled: true
75
+ ignore_unspecified: false
74
76
 
75
77
  PropertySpelling:
76
78
  enabled: true
@@ -83,6 +85,10 @@ linters:
83
85
  Shorthand:
84
86
  enabled: true
85
87
 
88
+ SingleLinePerProperty:
89
+ enabled: true
90
+ allow_single_line_rule_sets: true
91
+
86
92
  SingleLinePerSelector:
87
93
  enabled: true
88
94
 
@@ -108,12 +114,15 @@ linters:
108
114
  enabled: true
109
115
  style: single_quotes # or double_quotes
110
116
 
111
- TrailingSemicolonAfterPropertyValue:
117
+ TrailingSemicolon:
112
118
  enabled: true
113
119
 
114
120
  UnnecessaryMantissa:
115
121
  enabled: true
116
122
 
123
+ UnnecessaryParentReference:
124
+ enabled: true
125
+
117
126
  UrlFormat:
118
127
  enabled: true
119
128
 
@@ -0,0 +1,99 @@
1
+ # Concentric CSS
2
+ # http://rhodesmill.org/brandon/2011/concentric-css/
3
+
4
+ display
5
+ position
6
+ top
7
+ right
8
+ bottom
9
+ left
10
+
11
+ float
12
+ clear
13
+
14
+ visibility
15
+ opacity
16
+ z-index
17
+
18
+ margin
19
+ margin-top
20
+ margin-right
21
+ margin-bottom
22
+ margin-left
23
+
24
+ outline
25
+
26
+ border
27
+ border-top
28
+ border-right
29
+ border-bottom
30
+ border-left
31
+ border-width
32
+ border-top-width
33
+ border-right-width
34
+ border-bottom-width
35
+ border-left-width
36
+
37
+ border-style
38
+ border-top-style
39
+ border-right-style
40
+ border-bottom-style
41
+ border-left-style
42
+
43
+ border-color
44
+ border-top-color
45
+ border-right-color
46
+ border-bottom-color
47
+ border-left-color
48
+
49
+ background
50
+ background-color
51
+ background-image
52
+ background-repeat
53
+ background-position
54
+ cursor
55
+
56
+ padding
57
+ padding-top
58
+ padding-right
59
+ padding-bottom
60
+ padding-left
61
+
62
+ width
63
+ min-width
64
+ max-width
65
+
66
+ height
67
+ min-height
68
+ max-height
69
+
70
+ overflow
71
+
72
+ list-style
73
+ caption-side
74
+
75
+ table-layout
76
+ border-collapse
77
+ border-spacing
78
+ empty-cells
79
+
80
+ vertical-align
81
+
82
+ text-align
83
+ text-indent
84
+ text-transform
85
+ text-decoration
86
+
87
+ line-height
88
+ word-spacing
89
+ letter-spacing
90
+ white-space
91
+ color
92
+
93
+ font
94
+ font-family
95
+ font-size
96
+ font-weight
97
+
98
+ content
99
+ quotes
@@ -1,4 +1,5 @@
1
1
  require 'scss_lint/constants'
2
+ require 'scss_lint/exceptions'
2
3
  require 'scss_lint/config'
3
4
  require 'scss_lint/engine'
4
5
  require 'scss_lint/location'
@@ -12,8 +12,9 @@ module SCSSLint
12
12
  # Subset of semantic exit codes conforming to `sysexits` documentation.
13
13
  EXIT_CODES = {
14
14
  ok: 0,
15
+ warning: 1, # One or more warnings (but no errors) were reported
16
+ error: 2, # One or more errors were reported
15
17
  usage: 64, # Command line usage error
16
- data: 65, # User input was incorrect (i.e. contains lints)
17
18
  no_input: 66, # Input file did not exist or was not readable
18
19
  software: 70, # Internal software error
19
20
  config: 78, # Configuration error
@@ -93,11 +94,16 @@ module SCSSLint
93
94
  end
94
95
  end
95
96
 
96
- def run
97
+ def run # rubocop:disable MethodLength
97
98
  runner = Runner.new(@config)
98
99
  runner.run(files_to_lint)
99
100
  report_lints(runner.lints)
100
- halt :data if runner.lints.any?
101
+
102
+ if runner.lints.any? { |lint| lint.error? }
103
+ halt :error
104
+ elsif runner.lints.any?
105
+ halt :warning
106
+ end
101
107
  rescue InvalidConfiguration => ex
102
108
  puts ex
103
109
  halt :config
@@ -0,0 +1,4 @@
1
+ module SCSSLint::Exceptions
2
+ # Raised when an unexpected error occurs in a linter
3
+ class LinterError < StandardError; end
4
+ end
@@ -10,6 +10,8 @@ module SCSSLint
10
10
  @lints = []
11
11
  end
12
12
 
13
+ # Run this linter against a parsed document with a given configuration.
14
+ #
13
15
  # @param engine [Engine]
14
16
  # @param config [Config]
15
17
  def run(engine, config)
@@ -18,6 +20,12 @@ module SCSSLint
18
20
  visit(engine.tree)
19
21
  end
20
22
 
23
+ # Return the human-friendly name of this linter as specified in the
24
+ # configuration file and in lint descriptions.
25
+ def name
26
+ self.class.name.split('::')[2..-1].join('::')
27
+ end
28
+
21
29
  protected
22
30
 
23
31
  # Helper for creating lint from a parse tree node
@@ -28,7 +36,8 @@ module SCSSLint
28
36
  @lints << Lint.new(self,
29
37
  engine.filename,
30
38
  extract_location(node_or_line_or_location),
31
- message)
39
+ message,
40
+ @config.fetch('severity', :warning).to_sym)
32
41
  end
33
42
 
34
43
  # Extract {SCSSLint::Location} from a {Sass::Source::Range}.
@@ -3,34 +3,42 @@ module SCSSLint
3
3
  class Linter::CapitalizationInSelector < Linter
4
4
  include LinterRegistry
5
5
 
6
+ def visit_root(_node)
7
+ @ignored_names = Array(config['ignored_names']).to_set
8
+ @ignored_types = Array(config['ignored_types']).to_set
9
+ yield
10
+ end
11
+
6
12
  def visit_attribute(attribute)
7
- check(attribute)
13
+ check(attribute) unless @ignored_types.include?('attribute')
8
14
  end
9
15
 
10
16
  def visit_class(klass)
11
- check(klass)
17
+ check(klass) unless @ignored_types.include?('class')
12
18
  end
13
19
 
14
20
  def visit_element(element)
15
- check(element)
21
+ check(element) unless @ignored_types.include?('element')
16
22
  end
17
23
 
18
24
  def visit_id(id)
19
- check(id)
25
+ check(id) unless @ignored_types.include?('id')
20
26
  end
21
27
 
22
28
  def visit_placeholder(placeholder)
23
- check(placeholder)
29
+ check(placeholder) unless @ignored_types.include?('placeholder')
24
30
  end
25
31
 
26
32
  def visit_pseudo(pseudo)
27
- check(pseudo, 'Pseudo-selector')
33
+ check(pseudo, 'Pseudo-selector') unless @ignored_types.include?('pseudo-selector')
28
34
  end
29
35
 
30
36
  private
31
37
 
32
38
  def check(node, selector_name = nil)
33
39
  name = node.name.join
40
+
41
+ return if @ignored_names.include?(name)
34
42
  return unless name =~ /[A-Z]/
35
43
 
36
44
  selector_name ||= node.class.name.split('::').last
@@ -24,17 +24,24 @@ module SCSSLint
24
24
 
25
25
  def check_for_properties_with_mixins(node)
26
26
  prop_name = node.name.join
27
- return unless PROPERTIES_WITH_MIXINS.include?(prop_name)
27
+ return unless PROPERTIES_WITH_MIXINS.include?(prop_name) &&
28
+ !ignore_compass_mixin?(prop_name)
28
29
 
29
30
  add_lint node, "Use the Compass `#{prop_name}` mixin instead of the property"
30
31
  end
31
32
 
32
33
  def check_for_inline_block(node)
33
34
  prop_name = node.name.join
34
- return unless prop_name == 'display' && node.value.to_sass == 'inline-block'
35
+ return unless prop_name == 'display' &&
36
+ node.value.to_sass == 'inline-block' &&
37
+ !ignore_compass_mixin?('inline-block')
35
38
 
36
39
  add_lint node,
37
40
  'Use the Compass `inline-block` mixin instead of `display: inline-block`'
38
41
  end
42
+
43
+ def ignore_compass_mixin?(prop_name)
44
+ config.fetch('ignore', []).include?(prop_name)
45
+ end
39
46
  end
40
47
  end
@@ -4,7 +4,8 @@ module SCSSLint
4
4
  include LinterRegistry
5
5
 
6
6
  def visit_root(_node)
7
- @indent_width = config['width']
7
+ @indent_width = config['width'].to_i
8
+ @indent_character = config['character'] || 'space'
8
9
  @indent = 0
9
10
  yield
10
11
  end
@@ -27,14 +28,35 @@ module SCSSLint
27
28
  # sibling or its parent, as indentation isn't possible
28
29
  return if nodes_on_same_line?(previous_node(node), node)
29
30
 
31
+ if @indent_character == 'tab'
32
+ other_character = ' '
33
+ other_character_name = 'space'
34
+ else
35
+ other_character = "\t"
36
+ other_character_name = 'tab'
37
+ end
38
+
39
+ check_indent_width(node, other_character, @indent_character, other_character_name)
40
+ end
41
+
42
+ def check_indent_width(node, other_character, character_name, other_character_name)
30
43
  actual_indent = engine.lines[node.line - 1][/^(\s*)/, 1]
31
44
 
32
- return if actual_indent.length == @indent
45
+ if actual_indent.include?(other_character)
46
+ add_lint(node.line,
47
+ "Line should be indented with #{character_name}s, " \
48
+ "not #{other_character_name}s")
49
+ return true
50
+ end
51
+
52
+ unless actual_indent.length == @indent
53
+ add_lint(node.line,
54
+ "Line should be indented #{@indent} #{character_name}s, " \
55
+ "but was indented #{actual_indent.length} #{character_name}s")
56
+ return true
57
+ end
33
58
 
34
- add_lint(node.line,
35
- "Line should be indented #{@indent} spaces, " \
36
- "but was indented #{actual_indent.length} spaces")
37
- true
59
+ false
38
60
  end
39
61
 
40
62
  # Deal with `else` statements
@@ -3,11 +3,14 @@ module SCSSLint
3
3
  class Linter::PropertySortOrder < Linter
4
4
  include LinterRegistry
5
5
 
6
+ def visit_root(_node)
7
+ @preferred_order = extract_preferred_order_from_config
8
+ yield
9
+ end
10
+
6
11
  def visit_rule(node)
7
- # Ignore properties that contain interpolation
8
12
  sortable_props = node.children.select do |child|
9
- child.is_a?(Sass::Tree::PropNode) &&
10
- child.name.all? { |part| part.is_a?(String) }
13
+ child.is_a?(Sass::Tree::PropNode) && !ignore_property?(child)
11
14
  end
12
15
 
13
16
  sortable_prop_info = sortable_props
@@ -23,7 +26,7 @@ module SCSSLint
23
26
  sorted_props.each_with_index do |prop, index|
24
27
  next unless prop != sortable_prop_info[index]
25
28
 
26
- add_lint(sortable_props[index], MESSAGE)
29
+ add_lint(sortable_props[index], lint_message)
27
30
  break
28
31
  end
29
32
 
@@ -32,9 +35,6 @@ module SCSSLint
32
35
 
33
36
  private
34
37
 
35
- MESSAGE = 'Properties should be sorted in order, with vendor-prefixed ' \
36
- 'extensions before the standardized CSS property'
37
-
38
38
  # Compares two properties which can contain a vendor prefix. It allows for a
39
39
  # sort order like:
40
40
  #
@@ -53,8 +53,8 @@ module SCSSLint
53
53
  if a[:property] == b[:property]
54
54
  compare_by_vendor(a, b)
55
55
  else
56
- if config['order']
57
- compare_by_order(a, b, config['order'])
56
+ if @preferred_order
57
+ compare_by_order(a, b, @preferred_order)
58
58
  else
59
59
  a[:property] <=> b[:property]
60
60
  end
@@ -77,5 +77,57 @@ module SCSSLint
77
77
  (order.index(a[:property]) || Float::INFINITY) <=>
78
78
  (order.index(b[:property]) || Float::INFINITY)
79
79
  end
80
+
81
+ def extract_preferred_order_from_config
82
+ case config['order']
83
+ when nil
84
+ nil # No custom order specified
85
+ when Array
86
+ config['order']
87
+ when String
88
+ begin
89
+ file = File.open(File.join(SCSS_LINT_DATA,
90
+ 'property-sort-orders',
91
+ "#{config['order']}.txt"))
92
+ file.read.split("\n").reject { |line| line =~ /^(#|\s*$)/ }
93
+ rescue Errno::ENOENT
94
+ raise SCSSLint::LinterError,
95
+ "Preset property sort order '#{config['order']}' does not exist"
96
+ end
97
+ else
98
+ raise SCSSLint::LinterError,
99
+ 'Invalid property sort order specified -- must be the name of a '\
100
+ 'preset or an array of strings'
101
+ end
102
+ end
103
+
104
+ # Return whether to ignore a property in the sort order.
105
+ #
106
+ # This includes:
107
+ # - properties containing interpolation
108
+ # - properties not explicitly defined in the sort order (if ignore_unspecified is set)
109
+ def ignore_property?(prop_node)
110
+ return true if prop_node.name.any? { |part| !part.is_a?(String) }
111
+
112
+ config['ignored_unspecified'] &&
113
+ @preferred_order &&
114
+ !@preferred_order.include?(prop_node.name.join)
115
+ end
116
+
117
+ def preset_order?
118
+ config['order'].is_a?(String)
119
+ end
120
+
121
+ def lint_message
122
+ if preset_order?
123
+ "Properties should be sorted according to the #{config['order']} sort order"
124
+ elsif @preferred_order
125
+ 'Properties should be sorted according to the custom order ' \
126
+ 'specified by the configuration'
127
+ else
128
+ 'Properties should be sorted in order, with vendor-prefixed ' \
129
+ 'extensions before the standardized CSS property'
130
+ end
131
+ end
80
132
  end
81
133
  end