scss-lint 0.25.1 → 0.26.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 (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