scss-lint 0.16.1 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +7 -0
- data/lib/scss_lint/config.rb +25 -3
- data/lib/scss_lint/engine.rb +11 -0
- data/lib/scss_lint/linter.rb +23 -0
- data/lib/scss_lint/linter/selector_depth.rb +1 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +5 -9
- data/lib/scss_lint/linter/space_between_parens.rb +0 -15
- data/lib/scss_lint/linter/string_quotes.rb +75 -0
- data/lib/scss_lint/linter/url_quotes.rb +26 -0
- data/lib/scss_lint/reporter/files_reporter.rb +8 -0
- data/lib/scss_lint/runner.rb +3 -0
- data/lib/scss_lint/version.rb +1 -1
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee3e032cedba8042a60c3553efab8f57256e6d29
|
4
|
+
data.tar.gz: 4cc0c97f401c7d3fb69809a10a018ed6b963d5dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a96e204786ee1c9c03753f89e76aabd7db21a6a65f595cfa757f7b6b02c3d69cb1b7c1aac4cdc0a97cd39038e39fd7aacf41e9bced89fd7913681af4eb4dfc3a
|
7
|
+
data.tar.gz: 65f3a369a456c75dcbfaa2a046c210e276a6f8a32d18c37bc32a7e646269a350eb3a599e9953d622a94e984d03349392072d74b881ee54779d21a0bbfa690797
|
data/config/default.yml
CHANGED
@@ -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
|
|
data/lib/scss_lint/config.rb
CHANGED
@@ -62,16 +62,17 @@ module SCSSLint
|
|
62
62
|
file_contents = load_file_contents(file)
|
63
63
|
|
64
64
|
options =
|
65
|
-
if file_contents
|
66
|
-
|
65
|
+
if yaml = YAML.load(file_contents)
|
66
|
+
yaml.to_hash
|
67
67
|
else
|
68
|
-
|
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
|
|
data/lib/scss_lint/engine.rb
CHANGED
@@ -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
|
data/lib/scss_lint/linter.rb
CHANGED
@@ -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).
|
@@ -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?
|
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.
|
27
|
-
|
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
|
data/lib/scss_lint/runner.rb
CHANGED
@@ -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
|
data/lib/scss_lint/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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
|