scss-lint 0.16.1 → 0.17.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: 400884121e0e088fafe2953b2c2bf56957bff0e9
4
- data.tar.gz: 59f9d1d2dd3c53c29f58acb39c2281f75e8520ff
3
+ metadata.gz: ee3e032cedba8042a60c3553efab8f57256e6d29
4
+ data.tar.gz: 4cc0c97f401c7d3fb69809a10a018ed6b963d5dd
5
5
  SHA512:
6
- metadata.gz: 901f0882c448ba16faa3d5c6022004e06a883855fa25c21f97daf017e7793fb175ecc4475fea140e2a563742c7a44014e8bea8942a68f8a755da5af9271ec21c
7
- data.tar.gz: a08001bb95198be920e2daa8c830ae569177ed6c0aeaf3d2e3497d482d296e0c8326f24d8dedb75f8d0712e7e2fa30fed4ebad7601a2b2e56f2d15d47e2d1c18
6
+ metadata.gz: a96e204786ee1c9c03753f89e76aabd7db21a6a65f595cfa757f7b6b02c3d69cb1b7c1aac4cdc0a97cd39038e39fd7aacf41e9bced89fd7913681af4eb4dfc3a
7
+ data.tar.gz: 65f3a369a456c75dcbfaa2a046c210e276a6f8a32d18c37bc32a7e646269a350eb3a599e9953d622a94e984d03349392072d74b881ee54779d21a0bbfa690797
@@ -79,9 +79,16 @@ linters:
79
79
  enabled: true
80
80
  spaces: 0
81
81
 
82
+ StringQuotes:
83
+ enabled: true
84
+ style: single_quotes # or double_quotes
85
+
82
86
  TrailingSemicolonAfterPropertyValue:
83
87
  enabled: true
84
88
 
89
+ UrlQuotes:
90
+ enabled: true
91
+
85
92
  UsageName:
86
93
  enabled: true
87
94
 
@@ -62,16 +62,17 @@ module SCSSLint
62
62
  file_contents = load_file_contents(file)
63
63
 
64
64
  options =
65
- if file_contents.strip.empty?
66
- {}
65
+ if yaml = YAML.load(file_contents)
66
+ yaml.to_hash
67
67
  else
68
- YAML.load(file_contents).to_hash
68
+ {}
69
69
  end
70
70
 
71
71
  options = convert_single_options_to_arrays(options)
72
72
  options = extend_inherited_configs(options, file)
73
73
  options = merge_wildcard_linter_options(options)
74
74
  options = ensure_exclude_paths_are_absolute(options, file)
75
+ options = ensure_linter_exclude_paths_are_absolute(options, file)
75
76
  options
76
77
  end
77
78
 
@@ -131,6 +132,19 @@ module SCSSLint
131
132
  options
132
133
  end
133
134
 
135
+ def ensure_linter_exclude_paths_are_absolute(options, original_file)
136
+ options = options.dup
137
+
138
+ options['linters'] ||= {}
139
+
140
+ options['linters'].keys.each do |linter_name|
141
+ options['linters'][linter_name] =
142
+ ensure_exclude_paths_are_absolute(options['linters'][linter_name], original_file)
143
+ end
144
+
145
+ options
146
+ end
147
+
134
148
  # Ensure all excludes are absolute paths
135
149
  def ensure_exclude_paths_are_absolute(options, original_file)
136
150
  options = options.dup
@@ -227,6 +241,14 @@ module SCSSLint
227
241
  end
228
242
  end
229
243
 
244
+ def excluded_file_for_linter?(file_path, linter)
245
+ abs_path = File.expand_path(file_path)
246
+
247
+ linter_options(linter).fetch('exclude', []).any? do |exclusion_glob|
248
+ File.fnmatch(exclusion_glob, abs_path)
249
+ end
250
+ end
251
+
230
252
  def exclude_file(file_path)
231
253
  abs_path = File.expand_path(file_path)
232
254
 
@@ -1,6 +1,8 @@
1
1
  require 'sass'
2
2
 
3
3
  module SCSSLint
4
+ class FileEncodingError < StandardError; end
5
+
4
6
  # Contains all information for a parsed SCSS file, including its name,
5
7
  # contents, and parse tree.
6
8
  class Engine
@@ -20,6 +22,15 @@ module SCSSLint
20
22
 
21
23
  @lines = @contents.split("\n")
22
24
  @tree = @engine.to_tree
25
+ rescue Encoding::UndefinedConversionError, ArgumentError => error
26
+ if error.is_a?(Encoding::UndefinedConversionError) ||
27
+ error.message.include?('invalid byte sequence')
28
+ raise FileEncodingError,
29
+ "Unable to parse SCSS file: #{error.to_s}",
30
+ error.backtrace
31
+ else
32
+ raise
33
+ end
23
34
  end
24
35
  end
25
36
  end
@@ -41,6 +41,29 @@ module SCSSLint
41
41
  engine.lines[actual_line][actual_offset]
42
42
  end
43
43
 
44
+ # Extracts the original source code given a range.
45
+ def source_from_range(source_range)
46
+ current_line = source_range.start_pos.line - 1
47
+ last_line = source_range.end_pos.line - 1
48
+
49
+ source = engine.lines[current_line][(source_range.start_pos.offset - 1)..-1]
50
+
51
+ current_line += 1
52
+ while current_line < last_line
53
+ source += "#{engine.lines[current_line]}\n"
54
+ current_line += 1
55
+ end
56
+
57
+ if source_range.start_pos.line != source_range.end_pos.line &&
58
+ # Sometimes the parser reports ranges ending on the first column of the
59
+ # line after the last line; don't include the last line in this case.
60
+ engine.lines.count == current_line - 1
61
+ source += "#{engine.lines[current_line][0...source_range.end_pos.offset]}\n"
62
+ end
63
+
64
+ source
65
+ end
66
+
44
67
  # Monkey-patched implementation that adds support for traversing
45
68
  # Sass::Script::Nodes (original implementation only supports
46
69
  # Sass::Tree::Nodes).
@@ -39,6 +39,7 @@ module SCSSLint
39
39
  end
40
40
 
41
41
  parent_selectors = simple_sequences.count do |item|
42
+ next if item.is_a?(Array) # @keyframe percentages end up as Arrays
42
43
  item.rest.any? { |i| i.is_a?(Sass::Selector::Parent) }
43
44
  end
44
45
 
@@ -4,16 +4,14 @@ module SCSSLint
4
4
  include LinterRegistry
5
5
 
6
6
  def visit_rule(node)
7
- add_lint(node) if invalid_comma_placement? node
7
+ add_lint(node, MESSAGE) if invalid_comma_placement?(node)
8
8
  yield # Continue linting children
9
9
  end
10
10
 
11
- def description
12
- 'Each selector should be on its own line'
13
- end
14
-
15
11
  private
16
12
 
13
+ MESSAGE = 'Each selector in a comma sequence should be on its own line'
14
+
17
15
  # A comma is invalid if it starts the line or is not the end of the line
18
16
  def invalid_comma_placement?(node)
19
17
  normalize_spacing(condense_to_string(node.rule)) =~ /\n,|,[^\n]/
@@ -23,10 +21,8 @@ module SCSSLint
23
21
  # Sass::Script::Nodes, we need to condense it into a single string that we
24
22
  # can run a regex against.
25
23
  def condense_to_string(sequence_list)
26
- sequence_list.inject('') do |combined, string_or_script|
27
- combined +
28
- (string_or_script.is_a?(String) ? string_or_script : string_or_script.to_sass)
29
- end
24
+ sequence_list.select { |item| item.is_a?(String) }
25
+ .inject('') { |combined, item| combined + item }
30
26
  end
31
27
 
32
28
  # Removes extra spacing between lines in a comma-separated sequence due to
@@ -19,21 +19,6 @@ module SCSSLint
19
19
  end
20
20
  end
21
21
 
22
- #def visit_script_number(node)
23
- #puts node.class
24
- #puts node.node_parent.class
25
- #puts node.node_parent.node_parent.class
26
- #puts node.inspect
27
- #yield
28
- #end
29
-
30
- #def visit_script_operation(node)
31
- #puts node.node_parent.class
32
- #puts node.inspect
33
- #puts node.source_range.inspect
34
- #yield
35
- #end
36
-
37
22
  private
38
23
 
39
24
  def check(str, index, engine)
@@ -0,0 +1,75 @@
1
+ module SCSSLint
2
+ # Checks the type of quotes used in string literals.
3
+ class Linter::StringQuotes < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_script_string(node)
7
+ check_quotes(node, source_from_range(node.source_range))
8
+ end
9
+
10
+ def visit_import(node)
11
+ # @import source range conveniently includes only the quoted string
12
+ check_quotes(node, source_from_range(node.source_range))
13
+ end
14
+
15
+ def visit_charset(node)
16
+ # @charset source range includes entire declaration, so exclude '@charset' prefix
17
+ source = source_from_range(node.source_range)[('@charset'.length)..-1]
18
+
19
+ check_quotes(node, source)
20
+ end
21
+
22
+ private
23
+
24
+ def check_quotes(node, source)
25
+ source = source.strip
26
+ string = extract_string_without_quotes(source)
27
+ return unless string
28
+
29
+ case source[0]
30
+ when '"'
31
+ check_double_quotes(node, string)
32
+ when "'"
33
+ check_single_quotes(node, string)
34
+ end
35
+ end
36
+
37
+ STRING_WITHOUT_QUOTES_REGEX = %r{
38
+ \A
39
+ ["'](.*)["'] # Extract text between quotes
40
+ \s*\)?\s*;?\s* # Sometimes the Sass parser includes a trailing ) or ;
41
+ (//.*)? # Exclude any trailing comments that might have snuck in
42
+ \z
43
+ }x
44
+
45
+ def extract_string_without_quotes(source)
46
+ if match = STRING_WITHOUT_QUOTES_REGEX.match(source)
47
+ match[1]
48
+ end
49
+ end
50
+
51
+ def check_double_quotes(node, string)
52
+ if config['style'] == 'single_quotes'
53
+ add_lint(node, 'Prefer single quoted strings') if string !~ /'/
54
+ else
55
+ if string =~ /(?<! \\) \\"/x && string !~ /'/
56
+ add_lint(node, 'Use single-quoted strings when writing double quotes ' +
57
+ 'to avoid having to escape the double quotes')
58
+ end
59
+ end
60
+ end
61
+
62
+ def check_single_quotes(node, string)
63
+ if config['style'] == 'single_quotes'
64
+ if string =~ /(?<! \\) \\'/x && string !~ /"/
65
+ add_lint(node, 'Use double-quoted strings when writing single quotes ' +
66
+ 'to avoid having to escape the single quotes')
67
+ elsif string =~ /(?<! \\) \\"/x
68
+ add_lint(node, "Don't escape double quotes in single-quoted strings")
69
+ end
70
+ else
71
+ add_lint(node, 'Prefer double-quoted strings') if string !~ /"/
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ module SCSSLint
2
+ # Checks for quotes in URLs.
3
+ class Linter::UrlQuotes < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_prop(node)
7
+ case node.value
8
+ when Sass::Script::Tree::Literal
9
+ check(node, node.value.value.to_s)
10
+ when Sass::Script::Tree::ListLiteral
11
+ node.value.children.select { |child| child.is_a?(Sass::Script::Tree::Literal) }
12
+ .each { |child| check(node, child.value.to_s) }
13
+ end
14
+
15
+ yield
16
+ end
17
+
18
+ private
19
+
20
+ def check(node, string)
21
+ if string =~ /^\s*url\(\s*[^"']/
22
+ add_lint(node, 'URLs should be enclosed in quotes')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ module SCSSLint
2
+ # Reports a single line per file.
3
+ class Reporter::FilesReporter < Reporter
4
+ def report_lints
5
+ lints.map(&:filename).uniq.join("\n") + "\n" if lints.any?
6
+ end
7
+ end
8
+ end
@@ -34,6 +34,7 @@ module SCSSLint
34
34
 
35
35
  @linters.each do |linter|
36
36
  next unless config.linter_enabled?(linter)
37
+ next if config.excluded_file_for_linter?(file, linter)
37
38
 
38
39
  begin
39
40
  linter.run(engine, config.linter_options(linter))
@@ -46,6 +47,8 @@ module SCSSLint
46
47
  end
47
48
  rescue Sass::SyntaxError => ex
48
49
  @lints << Lint.new(ex.sass_filename, ex.sass_line, ex.to_s, :error)
50
+ rescue FileEncodingError => ex
51
+ @lints << Lint.new(file, 1, ex.to_s, :error)
49
52
  end
50
53
  end
51
54
  end
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module SCSSLint
3
- VERSION = '0.16.1'
3
+ VERSION = '0.17.0'
4
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.16.1
4
+ version: 0.17.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: 2014-01-07 00:00:00.000000000 Z
12
+ date: 2014-01-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 0.5.8
20
+ version: 0.6.0
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 0.5.8
27
+ version: 0.6.0
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: sass
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -59,14 +59,14 @@ dependencies:
59
59
  requirements:
60
60
  - - '='
61
61
  - !ruby/object:Gem::Version
62
- version: 2.13.0
62
+ version: 2.14.1
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - '='
68
68
  - !ruby/object:Gem::Version
69
- version: 2.13.0
69
+ version: 2.14.1
70
70
  description: Opinionated tool to help write clean and consistent SCSS
71
71
  email:
72
72
  - eng@causes.com
@@ -81,6 +81,7 @@ files:
81
81
  - lib/scss_lint/version.rb
82
82
  - lib/scss_lint/constants.rb
83
83
  - lib/scss_lint/utils.rb
84
+ - lib/scss_lint/reporter/files_reporter.rb
84
85
  - lib/scss_lint/reporter/xml_reporter.rb
85
86
  - lib/scss_lint/reporter/default_reporter.rb
86
87
  - lib/scss_lint/runner.rb
@@ -110,6 +111,7 @@ files:
110
111
  - lib/scss_lint/linter/space_after_property_name.rb
111
112
  - lib/scss_lint/linter/property_spelling.rb
112
113
  - lib/scss_lint/linter/usage_name.rb
114
+ - lib/scss_lint/linter/url_quotes.rb
113
115
  - lib/scss_lint/linter/space_between_parens.rb
114
116
  - lib/scss_lint/linter/border_zero.rb
115
117
  - lib/scss_lint/linter/space_after_property_colon.rb
@@ -117,6 +119,7 @@ files:
117
119
  - lib/scss_lint/linter/duplicate_property.rb
118
120
  - lib/scss_lint/linter/hex_format.rb
119
121
  - lib/scss_lint/linter/color_keyword.rb
122
+ - lib/scss_lint/linter/string_quotes.rb
120
123
  - lib/scss_lint/linter/leading_zero.rb
121
124
  - lib/scss_lint/linter/comment.rb
122
125
  - lib/scss_lint/linter/empty_rule.rb