scss-lint 0.13.0 → 0.14.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05986225943272819dc966c20d294f39f54bd7ef
4
- data.tar.gz: c5fb97b91c46be45d9a4bb72573356639e1f5afb
3
+ metadata.gz: e0c38097da925d1dcc77a8cc9c4d90468e598abb
4
+ data.tar.gz: e5e2f0352fc1e808b3ebe147c200c24727e425d0
5
5
  SHA512:
6
- metadata.gz: b38f342599f822cb5d26617986f264e5ef2ad00ddddc10c049a57369a191706e77b38b9a97b9735daaa424703d19b93829a33f0a09b2ebac208bec52a5874eba
7
- data.tar.gz: 45ecd691b23e170805dd1e45f509fbff7891c0f32880c1eeb4ff9d4967d7b7733128d1a3dcad4f367ac44309eca8ab79884dda18fe0436654c95d985a4f639b0
6
+ metadata.gz: 0e65ae3133dbdefac6e650b8561db659671280f659d050d37c5dc0704b3b592ac1d7ab8303a936ccaf0aa6fab6a02c50a2e8be254039e5cfc2caa741890cf600
7
+ data.tar.gz: e7677e4fe7bbc9337884d8cc423105a08d77361d9d50369ae41c4677677cf8626e920e791592d715db56d763dca2d91292469453a61b4201dbe66c1e3be25419
@@ -43,6 +43,10 @@ linters:
43
43
  PlaceholderInExtend:
44
44
  enabled: true
45
45
 
46
+ SelectorDepth:
47
+ enabled: true
48
+ max_depth: 3
49
+
46
50
  Shorthand:
47
51
  enabled: true
48
52
 
@@ -57,6 +57,10 @@ module SCSSLint
57
57
  @options[:excluded_files] = files
58
58
  end
59
59
 
60
+ opts.on('-f', '--format Formatter', 'Specify how to display lints', String) do |format|
61
+ set_output_format(format)
62
+ end
63
+
60
64
  opts.on('-i', '--include-linter linter,...', Array,
61
65
  'Specify which linters you want to run') do |linters|
62
66
  @options[:included_linters] = linters
@@ -78,10 +82,6 @@ module SCSSLint
78
82
  opts.on_tail('-v', '--version', 'Show version') do
79
83
  print_version opts.program_name, VERSION
80
84
  end
81
-
82
- opts.on('--xml', 'Output the results in XML format') do
83
- @options[:reporter] = SCSSLint::Reporter::XMLReporter
84
- end
85
85
  end
86
86
  end
87
87
 
@@ -175,6 +175,13 @@ module SCSSLint
175
175
  print output if output
176
176
  end
177
177
 
178
+ def set_output_format(format)
179
+ @options[:reporter] = SCSSLint::Reporter.const_get(format + 'Reporter')
180
+ rescue NameError
181
+ puts "Invalid output format specified: #{format}"
182
+ halt :config
183
+ end
184
+
178
185
  def print_linters
179
186
  puts 'Installed linters:'
180
187
 
@@ -68,27 +68,49 @@ module SCSSLint
68
68
  YAML.load(file_contents).to_hash
69
69
  end
70
70
 
71
+ options = convert_single_options_to_arrays(options)
72
+ options = extend_inherited_configs(options, file)
73
+ options = merge_wildcard_linter_options(options)
74
+ options = ensure_exclude_paths_are_absolute(options, file)
75
+ options
76
+ end
77
+
78
+ # Convert any config options that accept a single value or an array to an
79
+ # array form so that merging works.
80
+ def convert_single_options_to_arrays(options)
81
+ options = options.dup
82
+
71
83
  if options['exclude']
72
84
  # Ensure exclude is an array, since we allow user to specify a single
73
85
  # string. We do this before merging with the config loaded via
74
- # inherit_form since this allows us to merge the excludes from that,
86
+ # inherit_from since this allows us to merge the excludes from that,
75
87
  # rather than overwriting them.
76
88
  options['exclude'] = [options['exclude']].flatten
77
89
  end
78
90
 
79
- if options['inherit_from']
80
- includes = [options.delete('inherit_from')].flatten.map do |include_file|
81
- load_options_hash_from_file(path_relative_to_config(include_file, file))
82
- end
91
+ options
92
+ end
83
93
 
84
- merged_includes = includes[1..-1].inject(includes.first) do |merged, include_file|
85
- smart_merge(merged, include_file)
86
- end
94
+ # Loads and extends a list of inherited options with the given options.
95
+ def extend_inherited_configs(options, original_file)
96
+ return options unless options['inherit_from']
97
+ options = options.dup
87
98
 
88
- options = smart_merge(merged_includes, options)
99
+ includes = [options.delete('inherit_from')].flatten.map do |include_file|
100
+ load_options_hash_from_file(path_relative_to_config(include_file, original_file))
89
101
  end
90
102
 
91
- # Merge options from wildcard linters into individual linter configs
103
+ merged_includes = includes[1..-1].inject(includes.first) do |merged, include_file|
104
+ smart_merge(merged, include_file)
105
+ end
106
+
107
+ smart_merge(merged_includes, options)
108
+ end
109
+
110
+ # Merge options from wildcard linters into individual linter configs
111
+ def merge_wildcard_linter_options(options)
112
+ options = options.dup
113
+
92
114
  options.fetch('linters', {}).keys.each do |class_name|
93
115
  next unless class_name.include?('*')
94
116
 
@@ -106,7 +128,13 @@ module SCSSLint
106
128
  end
107
129
  end
108
130
 
109
- # Ensure all excludes are absolute paths
131
+ options
132
+ end
133
+
134
+ # Ensure all excludes are absolute paths
135
+ def ensure_exclude_paths_are_absolute(options, original_file)
136
+ options = options.dup
137
+
110
138
  if options['exclude']
111
139
  excludes = [options['exclude']].flatten
112
140
 
@@ -115,7 +143,7 @@ module SCSSLint
115
143
  exclusion_glob
116
144
  else
117
145
  # Expand the path assuming it is relative to the config file itself
118
- File.expand_path(exclusion_glob, File.expand_path(File.dirname(file)))
146
+ File.expand_path(exclusion_glob, File.expand_path(File.dirname(original_file)))
119
147
  end
120
148
  end
121
149
  end
@@ -1,6 +1,8 @@
1
1
  require 'sass'
2
2
 
3
3
  module SCSSLint
4
+ # Contains all information for a parsed SCSS file, including its name,
5
+ # contents, and parse tree.
4
6
  class Engine
5
7
  ENGINE_OPTIONS = { cache: false, syntax: :scss }
6
8
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Stores information about a single problem that was detected by a [Linter].
2
3
  class Lint
3
4
  attr_reader :filename, :line, :description, :severity
4
5
 
@@ -11,7 +11,7 @@ module SCSSLint
11
11
  def visit_script_string(node)
12
12
  return unless node.type == :identifier
13
13
 
14
- remove_quoted_strings(node.value).scan(/[a-z]+/i) do |word|
14
+ remove_quoted_strings(node.value).scan(/(^|\s)([a-z]+)(?=\s|$)/i) do |_, word|
15
15
  add_color_lint(node, word) if color_keyword?(word)
16
16
  end
17
17
  end
@@ -10,8 +10,8 @@ module SCSSLint
10
10
  ]
11
11
 
12
12
  def visit_rule(node)
13
- children = node.children.select { |node| important_node?(node) }.
14
- map { |node| node.class }
13
+ children = node.children.select { |n| important_node?(n) }
14
+ .map { |n| n.class }
15
15
 
16
16
  sorted_children = children.sort do |a, b|
17
17
  DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b)
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for a property declared twice in a rule set.
2
3
  class Linter::DuplicateProperty < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for rules with no content.
2
3
  class Linter::EmptyRule < Linter
3
4
  include LinterRegistry
4
5
 
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Checks for hexadecimal colors that can be shortened.
2
3
  class Linter::HexFormat < Linter
3
4
  include LinterRegistry
4
5
 
@@ -0,0 +1,61 @@
1
+ module SCSSLint
2
+ # Checks for selectors with large depths of applicability.
3
+ class Linter::SelectorDepth < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_root(node)
7
+ @max_depth = config['max_depth']
8
+ @depth = 0
9
+ yield # Continue
10
+ end
11
+
12
+ def visit_rule(node)
13
+ old_depth = @depth
14
+ @depth = max_sequence_depth(node.parsed_rules, @depth)
15
+
16
+ if @depth > @max_depth
17
+ add_lint(node.parsed_rules || node,
18
+ 'Selector should have depth of applicability no greater ' <<
19
+ "than #{@max_depth}, but was #{@depth}")
20
+ end
21
+
22
+ yield # Continue linting children
23
+ @depth = old_depth
24
+ end
25
+
26
+ private
27
+
28
+ # Find the maximum depth of all sequences in a comma sequence.
29
+ def max_sequence_depth(comma_sequence, current_depth)
30
+ # Sequence contains interpolation; assume a depth of 1
31
+ return current_depth + 1 unless comma_sequence
32
+
33
+ comma_sequence.members.map { |sequence| sequence_depth(sequence, current_depth) }.max
34
+ end
35
+
36
+ def sequence_depth(sequence, current_depth)
37
+ separators, simple_sequences = sequence.members.partition do |item|
38
+ item.is_a?(String)
39
+ end
40
+
41
+ parent_selectors = simple_sequences.count { |item| item.to_s == '&' }
42
+
43
+ # Take the number of simple sequences and subtract one for each sibling
44
+ # combinator, as these "combine" simple sequences such that they do not
45
+ # increase depth.
46
+ depth = simple_sequences.size -
47
+ separators.count { |item| item == '~' || item == '+' }
48
+
49
+ if parent_selectors > 0
50
+ # If parent selectors are present, add the current depth for each
51
+ # additional parent selector.
52
+ depth += parent_selectors * (current_depth - 1)
53
+ else
54
+ # Otherwise this just descends from the containing selector
55
+ depth += current_depth
56
+ end
57
+
58
+ depth
59
+ end
60
+ end
61
+ end
@@ -24,8 +24,8 @@ module SCSSLint
24
24
  # can run a regex against.
25
25
  def condense_to_string(sequence_list)
26
26
  sequence_list.inject('') do |combined, string_or_script|
27
- combined + (string_or_script.is_a?(String) ? string_or_script
28
- : string_or_script.to_sass)
27
+ combined +
28
+ (string_or_script.is_a?(String) ? string_or_script : string_or_script.to_sass)
29
29
  end
30
30
  end
31
31
 
@@ -1,4 +1,6 @@
1
1
  module SCSSLint
2
+ # Checks for variable/function/mixin/placeholder uses which contains uppercase
3
+ # characters or underscores.
2
4
  class Linter::UsageName < Linter
3
5
  include LinterRegistry
4
6
 
@@ -1,6 +1,7 @@
1
1
  module SCSSLint
2
2
  class NoSuchLinter < StandardError; end
3
3
 
4
+ # Stores all linters available to the application.
4
5
  module LinterRegistry
5
6
  @linters = []
6
7
 
@@ -16,7 +17,7 @@ module SCSSLint
16
17
  begin
17
18
  Linter.const_get(linter_name)
18
19
  rescue NameError
19
- raise NoSuchLinter.new("Linter #{linter_name} does not exist")
20
+ raise NoSuchLinter, "Linter #{linter_name} does not exist"
20
21
  end
21
22
  end
22
23
  end
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Responsible for displaying lints to the user in some format.
2
3
  class Reporter
3
4
  attr_reader :lints
4
5
 
@@ -1,6 +1,7 @@
1
1
  require 'colorize'
2
2
 
3
3
  module SCSSLint
4
+ # Reports a single line per lint.
4
5
  class Reporter::DefaultReporter < Reporter
5
6
  def report_lints
6
7
  if lints.any?
@@ -1,4 +1,5 @@
1
1
  module SCSSLint
2
+ # Reports lints in an XML format.
2
3
  class Reporter::XMLReporter < Reporter
3
4
  def report_lints
4
5
  output = '<?xml version="1.0" encoding="utf-8"?>'
@@ -1,4 +1,7 @@
1
1
  module Sass::Script
2
+ # Ignore documentation lints as these aren't original implementations.
3
+ # rubocop:disable Documentation
4
+
2
5
  # Redefine some of the lexer helpers in order to store the original string
3
6
  # with the created object so that the original string can be inspected rather
4
7
  # than a typically normalized version.
@@ -3,6 +3,9 @@
3
3
  # breaks the Sass compiler, but since we're only doing lints this is fine for
4
4
  # now.
5
5
  module Sass::Tree
6
+ # Ignore documentation lints as these aren't original implementations.
7
+ # rubocop:disable Documentation
8
+
6
9
  # Define some common helper code for use in the various monkey patchings.
7
10
  class Node
8
11
  # Stores node for which this node is a direct child
@@ -1,3 +1,4 @@
1
+ # Defines the gem version.
1
2
  module SCSSLint
2
- VERSION = '0.13.0'
3
+ VERSION = '0.14.0'
3
4
  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.13.0
4
+ version: 0.14.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-12-03 00:00:00.000000000 Z
12
+ date: 2013-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize
@@ -103,6 +103,7 @@ files:
103
103
  - lib/scss_lint/linter/declaration_order.rb
104
104
  - lib/scss_lint/linter/zero_unit.rb
105
105
  - lib/scss_lint/linter/placeholder_in_extend.rb
106
+ - lib/scss_lint/linter/selector_depth.rb
106
107
  - lib/scss_lint/linter/compass/property_with_mixin.rb
107
108
  - lib/scss_lint/linter/space_after_property_name.rb
108
109
  - lib/scss_lint/linter/usage_name.rb
@@ -141,9 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
142
  version: '0'
142
143
  requirements: []
143
144
  rubyforge_project:
144
- rubygems_version: 2.0.2
145
+ rubygems_version: 2.0.14
145
146
  signing_key:
146
147
  specification_version: 4
147
148
  summary: SCSS lint tool
148
149
  test_files: []
149
- has_rdoc: